파이썬 - 스레드 동기화

안녕하세요, 미래의 파이썬 마법사 여러분! 오늘, 우리는 스레드 동기화의 흥미로운 세계로 여행을 떠날 것입니다. 각 음악가가 스레드인 오케스트라를 지도하고, 모두 조화롭게 연주하도록 하는 것을 상상해보세요. 이것은 프로그래밍에서 스레드 동기화의 본질적인 것입니다!

Python - Synchronizing Threads

록을 사용한 스레드 동기화

우리의 동기화 도구킷에서 가장 기본적인 도구를 시작으로: 록을 사용해보겠습니다. 록을 호텔 객실 문에 붙어있는 "방해하지 마세요" 문구라고 생각해보세요. 스레드가 록을 확보하면, 그런 문구를 올리는 것과 같이 다른 스레드에게 "여, 여기서 바쁘니!"라고 말하는 것입니다.

이 개념을 설명하기 위한 간단한 예제를 보여드리겠습니다:

import threading
import time

# 공유 자원
counter = 0

# 록 생성
lock = threading.Lock()

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

# 두 개의 스레드 생성
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)

# 스레드 시작
thread1.start()
thread2.start()

# 스레드가 끝날 때까지 기다리기
thread1.join()
thread2.join()

print(f"최종 카운터 값: {counter}")

이 예제에서, 두 개의 스레드가 공유 자원 counter를 증가시키려고 시도하고 있습니다. 록을 사용하지 않으면, 두 스레드가 동시에 counter를 증가시키려고 하며, 이는 잘못된 결과로 이어질 수 있는 경쟁 조건이 발생할 수 있습니다.

lock.acquire()를 사용하여 counter를 수정하기 전에 록을 확보하고, lock.release()를 사용하여 록을 반납함으로써, 한 번에 하나의 스레드만 counter를 증가시킬 수 있도록 합니다. 이는 릴레이 레이스에서 배틀론을 전달하는 것과 같습니다 - 록을 든 스레드만이 공유 자원을 수정할 수 있습니다.

조건 객체를 사용한 파이썬 스레드 동기화

이제 조건 객체를 사용하여 우리의 동기화 게임을 한 단계 업그레이드해보겠습니다. 이 객체는 스레드에게 더 복잡한 조정을 허용하는 정교한 교통 신호등과 같습니다.

생산자-소비자 시나리오를 조건 객체를 사용하여 보여드리겠습니다:

import threading
import time
import random

# 공유 버퍼
buffer = []
MAX_SIZE = 5

# 조건 객체 생성
condition = threading.Condition()

def producer():
global buffer
while True:
with condition:
while len(buffer) == MAX_SIZE:
print("버퍼 가득차, 프로듀서가 기다리는 중...")
condition.wait()
item = random.randint(1, 100)
buffer.append(item)
print(f"생산된 항목: {item}")
condition.notify()
time.sleep(random.random())

def consumer():
global buffer
while True:
with condition:
while len(buffer) == 0:
print("버퍼 비어있음, 소비자가 기다리는 중...")
condition.wait()
item = buffer.pop(0)
print(f"소비된 항목: {item}")
condition.notify()
time.sleep(random.random())

# 프로듀서와 소비자 스레드 생성
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 스레드 시작
producer_thread.start()
consumer_thread.start()

# 잠시 동안 실행
time.sleep(10)

이 예제에서, 프로듀서가 항목을 버퍼에 추가하고, 소비자가 버퍼에서 항목을 제거하고 있습니다. 조건 객체는 그들의 행동을 조정하는 데 도움을 줍니다:

  • 버퍼가 가득 차면 프로듀서가 기다립니다.
  • 버퍼가 비어 있으면 소비자가 기다립니다.
  • 안전하게 진행할 수 있을 때 서로에게 알립니다.

이는 조건 객체가 코디오라는 것과 같은 잘 coreographed 무용으로!

join() 메서드를 사용한 스레드 동기화

join() 메서드는 한 스레드가 다른 스레드가 연주를 끝내기를 기다리고 나서舞台에 나오는 것과 같습니다. 이는 간단하지만 강력한 동기화 방법입니다.

예제를 보여드리겠습니다:

import threading
import time

def worker(name, delay):
print(f"{name} 시작 중...")
time.sleep(delay)
print(f"{name} 완료!")

# 스레드 생성
thread1 = threading.Thread(target=worker, args=("스레드 1", 2))
thread2 = threading.Thread(target=worker, args=("스레드 2", 4))

# 스레드 시작
thread1.start()
thread2.start()

# 스레드1이 끝날 때까지 기다리기
thread1.join()
print("메인 스레드가 스레드1 뒤에 기다리는 중")

# 스레드2가 끝날 때까지 기다리기
thread2.join()
print("메인 스레드가 스레드2 뒤에 기다리는 중")

print("모든 스레드가 끝났습니다!")

이 예제에서, 메인 스레드는 두 개의 작업자 스레드를 시작한 후, 각각을 join()을 사용하여 기다립니다. 이는 부모가 자식들이 숙제를 끝내기를 기다리고 나서 저녁 식사를 제공하는 것과 같습니다!

추가 동기화 원语

파이썬은 스레드 동기화를 위한 여러 가지 다른 도구를 제공합니다. 몇 가지를 빠르게 살펴보겠습니다:

원语 설명 사용 사례
세마포어 일정 수의 스레드가 자원에 접근할 수 있도록 허용 데이터베이스 연결 풀 관리
이벤트 한 스레드가 다른 스레드에게 이벤트를 시그널링 작업이 완료되었음을 시그널링
배리어 여러 스레드가 모두 특정 지점에 도달할 때까지 기다립니다 레이스 시작 동기화

세마포어를 사용한 빠른 예제를 보여드리겠습니다:

import threading
import time

# 한 번에 두 개의 스레드에게 허용하는 세마포어 생성
semaphore = threading.Semaphore(2)

def worker(name):
with semaphore:
print(f"{name} 세마포어를 확보")
time.sleep(1)
print(f"{name} 세마포어를 반납")

# 5개의 스레드 생성 및 시작
threads = []
for i in range(5):
thread = threading.Thread(target=worker, args=(f"스레드 {i}",))
threads.append(thread)
thread.start()

# 모든 스레드가 끝날 때까지 기다리기
for thread in threads:
thread.join()

print("모든 스레드가 끝났습니다!")

이 예제에서, 세마포어는 클럽의 보안원처럼 한 번에 두 개의 스레드만 들어올 수 있도록 합니다. 이는 모자라는 자원에 접근을 제한하는 상황에 완벽합니다!

그리고 여기서 끝입니다, 여러분! 우리는 파이썬에서 흥미로운 스레드 동기화 세계를 탐험했습니다. 기억해주세요, 오케스트라를 지도하거나 무용을 coreographing하는 것처럼, 스레드 동기화는 모두 협조와 시간감에 관련이 있습니다. 이러한 도구들을 프로그래밍 도구킷에 갖추고 있으며, 조화로운 멀티스레드 파이썬 프로그램을 작성하는 데 도움이 될 것입니다. 연습을 계속하고, 호기심을 유지하며, 즐거운 코딩 되세요!

Credits: Image by storyset