파이썬 멀티 쓰레드 예제 정리 ( Join, Lock, Queue )

2023. 4. 6. 22:51it

반응형

 

0. 서론

threading 모듈을 이용하여 스레드를 생성하고, start() 메서드를 호출하여 스레드를 실행합니다.
join() 메서드를 이용하여 스레드의 실행이 끝날 때까지 대기하거나, 반환 값을 받을 수 있습니다.
Lock 객체를 이용하여 여러 스레드에서 공유하는 자원에 대한 동기화를 수행할 수 있습니다.
Queue 객체를 이용하여 스레드 간 데이터를 전달하거나 작업을 분배할 수 있습니다.

 

1-1. 기본적인 스레드 생성과 실행

728x90
import threading

def worker(num):
    print(f"Worker {num} started")
    print(f"Worker {num} finished")

for i in range(5):
    t = threading.Thread(target=worker, args=(i,))
    t.start()


이 예제는 worker() 함수를 target으로 하는 스레드를 5개 생성하고 실행하는 예제입니다.

worker() 함수는 인자로 받은 num 값을 이용하여 실행될 때마다 해당하는 스레드의 번호를 출력합니다.

 

for i in range(5): 구문에서는 0부터 4까지의 숫자를 반복적으로 생성하여, worker() 함수에게 전달하고 있습니다.

args 인자에 전달할 값은 튜플 형태로 전달되며, 이 때 튜플의 요소는 함수에 전달되는 인자 순서대로 정의됩니다. 따라서 args=(i,)는 worker() 함수에게 i 값을 전달하는 것입니다. 이렇게 생성된 스레드들은 병렬로 실행되며, 각각 worker() 함수에게 전달된 인자를 가지고 작업을 수행합니다.

 

1-2. join() 메서드를 이용한 스레드 실행 제어

import threading

def worker(num):
    print(f"Worker {num} started")
    print(f"Worker {num} finished")

threads = []
for i in range(5):
    t = threading.Thread(target=worker, args=(i,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print("All threads finished")


이 예제는 join() 메서드를 이용하여 모든 스레드가 실행을 완료할 때까지 기다리는 예제입니다.

worker() 함수는 예제 1과 동일하게 작동합니다.

 

1-3. Lock 객체를 이용한 동기화

반응형
import threading

counter = 0
lock = threading.Lock()

def worker():
    global counter
    lock.acquire()
    for i in range(100000):
        counter += 1
    lock.release()

threads = []
for i in range(5):
    t = threading.Thread(target=worker)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print(f"Counter: {counter}")


이 예제는 Lock 객체를 이용하여 여러 스레드에서 공유하는 counter 변수에 대한 동기화를 수행하는 예제입니다. worker() 함수는 counter 변수에 10만씩 더하는 작업을 5개의 스레드에서 동시에 수행합니다. 이 때 lock.acquire()과 lock.release() 메서드를 이용하여 각 스레드가 counter 변수에 접근할 때 상호배제(Mutual Exclusion)를 보장합니다.

 

1-4. Queue 객체를 이용한 스레드 간 데이터 전달

import threading
import queue

def producer(q):
    for i in range(1000):
        q.put(i)
    q.put(None)

def consumer(q):
    total = 0
    while True:
        item = q.get()
        if item is None:
            break
        total += item
    print("Total:", total)

if __name__ == "__main__":
    q = queue.Queue()
    t1 = threading.Thread(target=producer, args=(q,))
    t2 = threading.Thread(target=consumer, args=(q,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()


이 예제에서는 producer 스레드가 0부터 999까지의 숫자를 Queue 객체 q에 차례로 넣습니다. 그리고 마지막으로 None을 넣어서 작업이 끝났음을 알립니다. consumer 스레드는 무한 루프를 돌면서 Queue 객체 q에서 데이터를 하나씩 가져와서 합을 계산합니다. producer 스레드가 작업을 끝내고 None을 넣으면, consumer 스레드는 break문을 실행하고 루프를 빠져나와서 합을 출력합니다.

if __name__ == "__main__": 부분은 파이썬 모듈이 임포트되면 이 부분이 실행되지 않고, 직접 실행될 때만 실행되도록 하는 관례적인 코드입니다. 이 부분이 없으면, 다른 모듈에서 이 모듈을 임포트해서 사용할 때, 스레드가 두 개 생성되는 문제가 발생할 수 있습니다.

반응형