프로그래밍/Design Pattern

포이즌 필(Poison Pill) 패턴 : 멀티 스레드 프로세스를 안전하게 종료하는 방법

Lou Park 2025. 12. 28. 11:48

AI가 만들어낸 코드를 한 줄 한 줄 곱씹어 보던 중에 특이한 패턴을 발견했다.

@classmethod
def stop_processor(cls):
    """백그라운드 처리 스레드 중지"""
    cls._is_running = False
    cls._processing_queue.put(None)

 

나라면 스레드를 종료하기 위해서 그냥 is_running 플래그만 False로 바꿔놨을 것 같은데, 왜 Queue에다가 None이라는 이상한 값을 집어 넣는걸까?

 

포이즌 필 패턴

이는 안전하게 스레드를 종료하기 위한 포이즌 필(Poison Pill) 패턴이라고 한다.

Poison Pill

실생활에서 보이는 포이즌 필 패턴 예시가 재미있다.

식당에서 "CLOSED" 사인을 걸어두는 것을 예로들 수 있다. 영업종료 시간이 다가오면 식당주인은 문에 "CLOSED" 사인을 걸어둔다. 이는 더 이상 새로운 손님이 오는 것을 막지만, 지금 가게에 있는 손님들을 내쫓지는 않는다. 그들이 음식을 다 먹고, 계산하고 나갈 수 있도록 한다. 포이즌 필 메세지는 이렇듯 새로운 태스크가 처리되는 것을 막으면서도 현재 태스크를 마무리하고 안전하게 종료될 수 있도록 해준다.

 

포이즌 필 시퀀스 다이어그램 (https://java-design-patterns.com/patterns/poison-pill)

 

내 코드에서는 Queue에 들어갈 수 있는 메세지 타입들을 따로 정의한 바가 없었기때문에, AI는 편한 방법으로 "None"을 넣는 것을 택했던 것이다. (이놈!!! 헷갈렸잖아~)

@classmethod
def stop_processor(cls):
    """백그라운드 처리 스레드 중지"""
    cls._is_running = False
    cls._processing_queue.put(None)

@classmethod
def _process_queue(cls):
    """
    큐에서 하나씩 꺼내서 순차 처리
    """
    while cls._is_running:
        try:
            # 리플레이 요청 확인 (1초마다)
            cls.check_replay_requests()

            # 큐에서 항목 가져오기 (1초 타임아웃)
            try:
                item = cls._processing_queue.get(timeout=1)
            except:
                # 큐가 비어있으면 queue.Empty 예외 발생
                continue

            if item is None:
                break  # 종료 신호

            # 튜플이면 (type, entry_id) 형태
            if isinstance(item, tuple):
                item_type, entry_id = item
                if item_type == RouletteStatus.REPLAY.value:
                    cls._replay_roulette(entry_id)
            else:
                # 일반 entry_id
                cls._spin_roulette(item)

            time.sleep(1)
        except Exception as e:
            logger.error(f"룰렛 처리 오류: {e}")

 

_process_queue는 별도 스레드에서 돌아가는 코드다. 주석에 보이는 것 처럼 item이 None(포이즌 필)인지 체크 후 종료 신호로 판단되면 즉시 루프를 종료한다. 이 코드에서는 큐에서 item을 가져오는 1초동안 스레드가 블로킹이 되는데, 포이즌 필 메세지가 없었다면 다음 사이클에나 종료되었을 것이다. 포이즌 필 메세지로 인해 대기중이던 스레드가 즉시 깨어나서 원하는 타이밍에 안전하게 종료된다.

 

포이즌 필 패턴 사용시 주의할 점

컨슈머가 여러개인 경우, 컨슈머의 수 만큼 포이즌 필을 넣어주어야한다.

 

 

 

참고 자료
https://java-design-patterns.com/patterns/poison-pill/

반응형

'프로그래밍 > Design Pattern' 카테고리의 다른 글

메멘토 패턴 (Memento Pattern): Rust로 구현  (0) 2023.07.30