Python Threading

EBAB!·2024년 1월 26일
0

파이썬 threading 정리

logging 활용

기본적으로 스레드는 디버깅이 어렵기 때문에 logging을 통해 실행 과정을 잘 표현하는 것이 중요하다.

import logging


# 기본 설정
logging.basicConfig(
	level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='/path/to/logfile.log'
    datefmt='%Y-%m-%d %H:%M:%S'
    )

로그 레벨

DEBUG: 가장 낮은 레벨. 상세한 정보를 기록할 때 사용
INFO: 일반적인 정보를 기록할 때 사용
WARNING: 예상치 못한 일이 발생, 문제가 될만한 상황을 기록할 때 사용
ERROR: 프로그램의 일부 기능이 제대로 동작하지 않을 때 사용
CRITICAL: 프로그램 자체가 정상적으로 동작할 수 없을 때 사용



기본 실행

import logging
import threading

# 스레드 실행 함수
def thread_func(name):
    logging.info("Sub-Thread %s: starting", name)
    """
    Thread task code 
    """
    logging.info("Sub-Thread %s: finishing", name)


# 메인스레드 영역
if __name__ == "__main__":
    # Logging format 설정
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
    logging.info("Main-Thread : before creating thread")

    # 함수 인자 확인
    x = threading.Thread(target=thread_func, args=("First",))

    logging.info("Main-Thread : before running thread")

    # 서브 스레드 시작
    x.start()

    logging.info("Main-Thread : wait for the thread to finish")

    # x.join()  # x 스레드 종료까지 메인스레드 대기

    logging.info("Main-Thread : all done")


Daemon Thread

메인 스레드 종료 시 서브스레드 함께 종료

import logging
import threading

# 스레드 실행 함수
def thread_func(name):
    logging.info("Sub-Thread %s: starting", name)
    """
    Thread task code 
    """
    logging.info("Sub-Thread %s: finishing", name)


# 메인스레드 영역
if __name__ == "__main__":
    # Logging format 설정
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
    logging.info("Main-Thread : before creating thread")

    # 방법 1. 직접 daemon 옵션 추가
    x = threading.Thread(target=thread_func, args=("First",), daemon=True)
    # 방법 2. 속성 설정
    # x = .Thread(target=thread_func, args=("First",))
    # x.daemon = True
    
    logging.info("Main-Thread : before running thread")

    # 서브 스레드 시작
    x.start()

    logging.info("Main-Thread : wait for the thread to finish")

    logging.info("Main-Thread : all done")
    # 이 시점(메인스레드 종료)에서 서브스레드 강제 종료


ThreadPoolExcuter

import logging
from concurrent.futures import ThreadPoolExecutor

# 스레드 실행 함수
def task(name):
    logging.info("Sub-Thread %s: starting", name)
	"""
    Thread task
    """
    logging.info("Sub-Thread %s: finishing result: %d", name, result)

    return task_result


# 메인 영역
def main():
    # Logging format 설정
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")

    logging.info("Main-Thread : before creating and running thread")

    """     방법1. 직접 스레드 추가     """
    # max_workers : 작업의 개수가 넘어가면 직접 설정이 유리
    executor = ThreadPoolExecutor(max_workers=3)
    
    task1 = executor.submit(task, ('First',))
    task2 = executor.submit(task, ('Second',))

    # 결과 값 있을 경우
    # print(task1.result())
    # print(task2.result())

    

    """     방법2. with 구문 사용     """
    with ThreadPoolExecutor(max_workers=3) as executor:
    	# 곧바로 결과값 받아오기
        tasks = executor.map(task, ['First', 'Second'])
        
        # 결과값 리스트 확인
        # print(list(tasks))  
        
        # 스레드 객체 리스트 받기
        threads = [excuter.submit(task, idx) for idx in range(5)]
        
        # 스레드 객체를 통한 결과값 확인
        # print([t.result() for t in threads])

    logging.info("Main-Thread : all done")

if __name__ == '__main__':
    main()
  

ThreadPoolExecutor 내부 동작 과정

1. 작업 제출

  • 사용자는 ThreadPoolExecutor의 submit 또는 map 메서드를 사용해 작업을 제출.
  • 제출된 각 작업은 내부적으로 Future 객체로 변환.
    • Future 객체는 작업의 상태를 추적하고 결과를 저장
  • Future 객체는 실행을 대기 중인 작업 목록을 관리하는 큐에 추가됨.

2. 큐 관리

ThreadPoolExecutor는 내부적으로 작업 대기열로서 큐를 사용

  • 스레드 풀의 스레드 중 하나가 큐에서 작업을 가져감. (이 과정은 스레드가 사용 가능할 때까지 자동)

3. 작업 실행

  • 스레드 풀의 스레드는 큐에서 가져온 작업을 실행.
  • 작업이 완료되면 해당 작업에 연결된 Future 객체는 작업의 결과 또는 발생한 예외를 저장.

4. 결과 반환

  • 사용자는 Future 객체의 result 메서드를 호출하여 작업의 결과를 받음.
  • 작업이 아직 완료되지 않았다면, result 메서드는 작업이 완료될 때까지 대기.
profile
공부!

0개의 댓글