queue 내장 모듈의 Queue 클래스를 사용해서 파이프라인을 스레드로 실행하게 구현
아래와 같은 문제 해결
Simulate_thread 함수의 방식보다 simulate pipeline함수가 더 따라가기 어렵다
코드의 가독성을 개선하려면 ClosableQueue와 StoppableWorker라는 추가 지원 클래스가 필요하게 되고, 복잡도도 함께 증가한다.
병렬성을 활용하여 필요에 따라서 자동으로 시스템 규모가 확장되지않는다.
디버깅을 활성화하려면 발생한 예외를 작업 스레드에서 수동으로 잡아서 Queue를 통해 전달하고 주 스레드에서 다시 발생 시킨다.
그러나, 결국 요구 사항이 다시 변경했을 때 큰 문제들이 나타나게 된다.
def count_neighbors(y,x,get):
#여기서 블로킹 I/O를 수행
data = my_socket.recv(100)
#위의 함수를 병렬화하기 위해서 파이프라인 추가
# 작업자 스레드 사이에서 예외가 제대로 전달하여 주 스레드까지 도달하는지 확인
# 동기화를 위해서 Grid 클래스의 Lock사용
def count_neighbors_thread(item):
y, x, state, get = item
try:
neighbors = count_neighbors(y,x,get)
except Exception as e:
neighbors = 3
return (y,x,state, neighbors)
def game_logic_thread(item):
y , x, state, neighbors = item
if isinstance(neighbors, Exception):
next_state = neighbors
else:
try:
next_state = game_logic(state, neighbors)
except Exception as e:
next_state = 0
return (y, x, next_state)
class LockingGrid(Grid):
#count_neighbors_thread 작업자와 그에 해당하는 Thread 인스턴스를 위해서
#다른 Queue 인스턴스 집합 만들기
in_queue = ClosableQueue()
logic_queue = ClosableQueue()
out_queue = ClosableQueue()
threads = []
for _ in range(5):
thread = StoppableWorker(count_neighbors_thread, in_queue, logic_queue)
thread.start()
threads.append(thread)
for _ in range(5):
thread = StoppableWorker(game_logic_thread, logic_queue, out_queue)
thread.start()
threads.append(thread)
중요 포인트
변경해야할 부분도 많고 코드도 많이 필요하다
Queue가 팬아웃과 팬인 문제를 해결하지만 부가 비용이 많다.
Thread만 사용하는 방식보다 Queue를 사용하는 방식이 더 좋지만 Queue는 파이썬이 제공하는 다른 도구만큼 좋지 않다>
작업자 스레드 수를 고정하고 Queue와 함께 사용하면 스레드를 사용할 때 팬인과 팬아웃의 규모 확장성을 개선한다.
Queue를 사용하도록 기존 코드를 리팩터링하려면 상당히 많은 작업이 필요한데 다단계로 이뤄진 파이프라인이 필요하면 작업량이 더 늘어난다.
다른 파이썬 내장 기능이나 모듈이 제공하는 병렬 I/O를 가능하게 해주는 다른 기능과 비교 시, Queue는 프로그램이 활용할 수 있는 전체 I/O병렬성의 정도를 제한한다.