Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬(Python)에서 스레드의 예외를 호출자 스레드에서 잡는 방법

thread_obj.start()가 즉시 반환된다는 점이 핵심 문제입니다. 시작된 자식 스레드는 자신만의 독립적인 컨텍스트와 스택에서 실행되며, 그곳에서 발생하는 모든 예외 역시 자식 스레드의 컨텍스트에 속하게 됩니다.

따라서 부모 스레드(호출자)가 이 예외를 알 수 있게 하려면, 메시지 전달 방식으로 예외 정보를 부모 스레드에 넘겨주어야 합니다. 가장 일반적인 방법은 queue.Queue를 사용하여 예외 정보(sys.exc_info())를 전달하는 것입니다.

예외를 큐로 전달하는 구현 예제

아래와 같이 코드를 다시 작성할 수 있습니다:

import sys
import threading
import queue

class ExcThread(threading.Thread):
    def __init__(self, foo):
        threading.Thread.__init__(self)
        self.foo = foo

    def run(self):
        try:
            raise Exception('An error occurred here.')
        except Exception:
            # 예외 정보를 큐에 담아 부모 스레드에 전달
            self.foo.put(sys.exc_info())

def main():
    foo = queue.Queue()
    thread_obj = ExcThread(foo)
    thread_obj.start()

    while True:
        try:
            exc = foo.get(block=False)
        except queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            print(exc_type, exc_obj)
            print(exc_trace)

        thread_obj.join(0.1)
        if thread_obj.is_alive():
            continue

코드 동작 원리

1. 예외 캡처: 자식 스레드의 run() 메서드 내부에서 try-exceptsys.exc_info()를 사용해 예외 유형(type), 예외 객체(object), 트레이스백(traceback)을 포함한 전체 정보를 큐에 저장합니다.

2. 예외 수신: 메인 스레드는 while 루프를 돌며 큐를 확인합니다. block=False 옵션으로 논블로킹 방식으로 데이터를 가져오고, 큐가 비어 있으면 queue.Empty 예외를 무시합니다.

3. 종료 대기: thread_obj.join(0.1)로 최대 0.1초간 자식 스레드의 종료를 기다리고, 아직 살아 있다면(is_alive()) 루프를 계속 반복합니다.


참고 사항

위 코드는 파이썬 3 기준으로 작성되었습니다. 파이썬 2에서는 queue 모듈 대신 Queue 모듈을 사용하고, is_alive() 대신 isAlive()를 사용하십시오.

또한 실무에서는 폴링(polling) 방식보다 queue.get(block=True) 또는 타임아웃을 활용하는 편이 CPU 자원을 절약할 수 있으며, concurrent.futures.ThreadPoolExecutor를 사용하면 future.result() 호출 시 자동으로 자식 스레드의 예외가 재발생되므로 더 간결하게 처리할 수 있습니다.