📌 Python Thread2

  • join()
    - 현재 실행되고 있는 thread가 종료될 때까지 main thread인 deamon thread를 종료시키지 않는다.
import time
import threading

def cal_show():
    now = time.localtime()
    a = "오전 "+str(now[3])+":"+str(now[4])+":"+str(now[5])
    print('%8s' %(a))
    print(f'{now[0]}-{now[1]}-{now[2]}')


def my_run():
    while True:
        now2 = time.localtime()
        if now2.tm_min == 31:
            break
        cal_show()
        print()
        time.sleep(1)

th = threading.Thread(target=my_run)
th.start()

th.join()
print('프로그램 종료')


📌 Python Thread lock(동기화)

  • 공유자원 충돌
    - 공유자원을 여러 thread가 사용하면 하나의 thread 수정 시점에 다른 thread에서 사용하는 공유자원이 비동기 처리된다.

  • 동기화
    - 하나의 thread가 자원을 사용하는 동안 다른 thread는 공유자원 사용을 대기하여 자원 충돌을 방지한다.
    - 동기화 == threading.Lock()

  • lock = threading.Lock()
    - thread를 lock시킬 객체를 반환
    - 하나의 thread의 시작 시점에 lock.acquire()을 걸고
    - 하나의 thread의 종료 시점에 lock.release()을 푼다.
import threading, time

g_count = 0     # 전역변수는 자동으로 thread의 공유자원이 된다.
lock = threading.Lock()

def threadCount(id, count):
    global g_count
    for i in range(count):
        lock.acquire()  # thread 간 충돌 방지용도. 현재 thread가 공유자원을 점유하고 있는 동안 다른 thread를 대기상태로 전환
        print(f'id:{id}, count:{i}, g_count:{g_count}')
        g_count += 1
        time.sleep(0.1)
        lock.release()  # 공유자원 점유 해제 (lock 해제)

for i in range(1, 6):
    threading.Thread(target=threadCount, args=(i, 5)).start()

time.sleep(5)
print('처리 후 최종 g_count : ', g_count)
print('프로그램 종료')


📌 Python Thread 활성화/비활성화

  • 활성화/비활성화
    - thread를 사용하는 클래스가 두개 있다고 할 때, lock을 이용한 동기화로는 공유자원에 대한 충돌을 예방할 수 없다.
    - 그래서 lock 동기화 + wait() 메소드를 통해 공유자원을 관리할 수 있다.

  • 동기화와 활성화, 비활성화
    - 동기화 : 여러 thread 간의 공유자원의 상태를 일치화시키는 것
    - 활성화 : 공유자원이 특정 상태가 되었을 때, 사용할 수 있도록 하는 것
    - 비활성화 : 공유자원의 특정 상태를 벗어날 경우, 해당 공유자원 사용을 사용하지 못하는 것
class Maker(threading.Thread):  # 생산자
    def run(self):
        global bread_plate
        for i in range(30):
            lock.acquire()  	# lock 걸기 (동기화)
            while bread_plate >= 10:
                print('빵 생산 초과로 대기')
                lock.wait()     # wait 기다리기 (비활성화)
            bread_plate += 1
            print(f'빵 생산 : {bread_plate}개')
            time.sleep(0.05)
            lock.notify()       # notify 진행하기 (활성화)
            lock.release()  	# lock 해제 (동기화 해제)

class Consumer(threading.Thread):   # 소비자
    def run(self):
        global bread_plate
        for i in range(30):
            lock.acquire()
            while bread_plate < 1:
                print('빵 다먹음')
                lock.wait()
            bread_plate -= 1
            print(f'빵 {bread_plate}개 남음')
            time.sleep(0.05)
            lock.notify()
            lock.release()

  • 응용
import threading, time

bread_plate = 0 # 빵접시 - 공유자원
lock = threading.Condition()

class Maker(threading.Thread):  # 생산자
    def run(self):
        global bread_plate
        for i in range(30):
            lock.acquire()  # lock 걸기
            while bread_plate >= 10:
                print('빵 생산 초과로 대기')
                lock.wait()     # thread의 비활성화
            bread_plate += 1
            print(f'빵 생산 : {bread_plate}개')
            time.sleep(0.05)
            lock.notify()       # thread의 활성화
            lock.release()  # lock 해제

class Consumer(threading.Thread):   # 소비자
    def run(self):
        global bread_plate
        for i in range(30):
            lock.acquire()
            while bread_plate < 1:
                print('빵 다먹음')
                lock.wait()
            bread_plate -= 1
            print(f'빵 {bread_plate}개 남음')
            time.sleep(0.05)
            lock.notify()
            lock.release()

mak = []
con = []

for i in range(5):
    mak.append(Maker())     # 생산자 개수
    con.append(Consumer())  # 소비자 개수

for th1 in mak:
    th1.start()

for th2 in con:
    th2.start()

for th1 in mak:
    th1.join()

for th2 in con:
    th2.join()

print('오늘 영업 끝~~')


📌 Python 실시간 채팅 구현 (Socket+Thread)

  • server 구현
    1. socket()
    2. bind()
    3. listen()
    4. while문 accpet()
    5. conn list에 저장
    6. thread 생성 후 함수로 conn 인자값 전달하기
    7. 함수에서 conn을 이용해 이름을 전체로 뿌려주고 while()문에서 client로부터 받은 메시지를 전체로 뿌려준다.
    8. except문에서 나간 사용자를 list에서 remove() 시키고, 전체 알림하기
import socket
import threading

so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
so.bind(('192.168.0.25', 5000))
so.listen(5)

users = []

def chat(conn):
    name = conn.recv(1024).decode('utf_8')
    try:
        notice = f'--------[ {name}님 입장 ]--------'
        print(notice)
        for user in users:
            user.send(notice.encode('utf_8'))

        while True:
            msg = conn.recv(1024).decode('utf_8')
            if not msg: continue
            data = f'{name}님 : {msg}'
            for user in users:
                user.send(data.encode('utf_8'))
    except:
        users.remove(conn)
        notice = f'        [ {name}님이 퇴장했습니다 ]        '
        print(notice)
        if users:
            for user in users:
                user.send(notice.encode('utf_8'))

while True:
    conn, addr = so.accept()
    users.append(conn)

    th = threading.Thread(target=chat, args=(conn,))
    th.start()
  • client 구현
    - 실시간 채팅 받기와 보내기가 동시에(multi tasking) 처리하기 위해서는 둘 중 하나의 기능을 thread로 처리해야 한다.
    1. 실시간 채팅 받기(무한 루프) 함수 만들기
    2. socket()
    3. connect()
    4. 채팅아이디 서버로 send()
    5. thread 만든 후 실시간 채팅 받기 함수 실행(socket 넘기기)
    6. while문에 실시간 채팅 넘기기 로직 구현
import socket
import threading
import sys

# 실시간 채팅 받기(무한 루프)
def chatClient(sock):
    while True:
        data = sock.recv(1024).decode('utf_8')
        if not data: continue
        print(data)
        sys.stdout.flush()

so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
so.connect(('192.168.0.25', 5000))

name = input('채팅 아이디 입력 : ')
so.send(name.encode('utf_8'))

th = threading.Thread(target=chatClient, args=(so,))
th.start()

# 실시간 채팅 보내기(무한 루프)
while True:
    sys.stdout.flush()
    msg = input()
    sys.stdout.flush()
    if not msg: continue
    so.send(msg.encode('utf_8'))


profile
데이터 사이언티스트를 목표로 하는 개발자

0개의 댓글