파이썬으로 프로그램을 만들다보면 thread를 사용하여야 하는 경우가 나옵니다. thread는 하나의 프로세스 안에서 일련의 코드를 동시에 실행시킬 수 있습니다. 특히 주기적으로 뭔가를 하는 경우에 thread가 많이 사용됩니다.
파이썬에도 thread를 사용할 수 있는 패키지가 지원됩니다.
몇가지 규칙만 지키면 되기 때문에 사용 방법은 간단합니다.
thread 만들기
thread를 만드는 부분에서는 threading.Thread 함수를 호출하면 됩니다. 이 때 실행할 함수명과 넘겨줄 인자를 기술하면 됩니다. 그런 후 my_thread.start()를 이용하여 실행하면 해당 thread가 동작을 합니다.
thread 마다 고유 이름이 할당되는군요. thread의 이름은 threading.currentThread().getName() 함수를 사용하여 얻을 수 있습니다.
thread 간에 공통으로 사용하는 변수를 수정하는 경우에는 접근성을 제한하여 합니다. 이것을 위하여 lock이라는 패키지를 사용하면 됩니다.
lock = threading.Lock()
lock.acquire()
공통변수 update
lock.release()
thread 4개가 동시에 돌아가면서 sum 이라는 변수에 값을 누적하는 예제입니다.
def execute(num) :
global sum
lock.acquire()
sum += num
lock.release()
print(threading.currentThread().getName(), num, sum)
for i in range(1,5) :
my_thread = threading.Thread(target=execute, args=(i,))
my_thread.start()
thread 간 통신하기
thread를 사용하여야 하는 경우에는 대부분 데이터 교환이 필요한 경우가 많습니다. 즉 서로 다른 thread끼리 데이터를 주고 받아야 하는 경우 뿐 아니라 특정 스레드에서 데이터가 올 때 까지 기다려야 하는 경우도 많이 있습니다.
이것을 위하여 event라는 함수가 사용됩니다.
데이터를 보내 주는 곳에서는 event를 하나 만들어서 공통 변수에 append를 하고 해당 event가 clear 될 때 까지 기다리고 있습니다. 데이터를 받는 곳에서는 공통 변수에 데이터가 있으면 공통 변수에서 데이터와 event 변수를 받아서 필요한 일을 한 후 이벤트를 처리하였음을 표시하고(evt.set()) 공통 변수에 있는 내용을 지우면 됩니다.
common_list = []
def consumer(que) :
while True :
if (len(que) > 0) :
recv = que[0]
data = recv[0]
evt = recv[1]
print("received ", data)
lock.acquire()
del(que[0])
evt.set()
lock.release()
def producer(que) :
for i in range(1,11) :
evt = threading.Event()
que.append([i, evt])
evt.wait()
print("event wait out")
my_thread2 = threading.Thread(target=consumer, args=(common_list,))
my_thread3 = threading.Thread(target=producer, args=(common_list,))
my_thread2.start()
my_thread3.start()
두 개의 thread가 돌고 있지만 서로가 잘 동기가 되어서 동작함을 확인할 수 있습니다.
조금 규모있는 프로그램을 개발하는 경우에 thread를 많이 사용합니다. thread 간의 동기화가 특히 중요하기 때문에, 각 thread 간의 동기와 다이어그램은 필수적입니다.