본문 바로가기
data science/python

Thread vs ThreadPool vs ThreadPoolExecutor

by 꼰대코더 2024. 5. 11.
  ThreadPool ThreadPool  ThreadPoolExecutor
생성쓰레드 수 수동 Thread 1 = Task 1개 파라미터로 지정가능
디폴트=(논리CPU수 + 4)
파라미터로 지정가능
디폴트=(논리CPU수 + 4)
최적의 용도 적은 수로 동시에 실행이 필요한 경우 모든 쓰레드가 동시에 실행되지는 않고 비어 있거나 I/O 등의 기다림이 있어 양보된 쓰레드에 실행되게 된다.
같은 태스크 내용으로 파라미터만 달리하여 많은 수의 쓰레드를 실행하는 경우.
모든 쓰레드가 동시에 실행되지는 않고 비어 있거나 I/O 등의 기다림이 있어 양보된 쓰레드에 실행되게 된다.
같은 태스크 내용으로 파라미터만 달리하여 많은 수의 쓰레드를 실행하는 경우.
결과의 리턴 불가능 가능 가능
취소기능 불가능 불가능 실행되기 전의 태스크에 한해
가능

※ ThreadPool 과 ThreadPoolExecutor 는 기본적으로 같은 기능이나 ThreadPoolExecutor 에 약간의 기능이 추가

 

Thread 의 코드 예

from threading import Thread

# 태스크 함수
def task(value):
      # 여기에 작업을 기술 ...
      # ...
      # 작업 끝
      print(f'.done {value}')


if __name__ == '__main__':
      # 0...19 의 파라미터를 같는 20개의 쓰레드를 생성
      threads = [Thread(target=task, args=(i,)) for i in range(20)]

      # 모든 쓰레드 시작
      for thread in threads:
           thread.start()
     
      # 모든 쓰레드가 종료되기까지 기다리기
      for thread in threads:
           thread.join()

      # report that all tasks are completed
      print('Done')

 

ThreadPool 의 코드 예

from multiprocessing.pool import ThreadPool

# 태스크 함수 
def task(value):
      # 여기에 작업을 기술 ...
      # ...
      # 작업 끝

      # 필요시 리턴값을 반환
      return value


if __name__ == '__main__':
      # 디폴트 수의 쓰레드의 풀을 생성 -> 지정 ThreadPool(processes=10)
      with ThreadPool() as pool:
             # 하나의 함수에 0...999 의 파라미터를 같는 100개의 태스크를 발행
             # 같은 함수에 다른 파라미터만 전달할 경우 map(함수명, 리스트(파라미터))
             for result in pool.map(task, range(100)):
                  # 결과를 처리
                  # 결과는 0..99 의 실행순서대로 출력
                  print(f'>got {result}')

      # 모든 태스크가 끝남
      print('Done')
# 독립적인 태스크를 호출시의 샘플
if __name__ == '__main__':
     pool = ThreadPool()

     # apply 를 사용 -> task 가 끝나지 전까지 호출측의 main 쓰레드는 블록이 된다.
     try:

          result = pool.apply(task, args=(10,))

          # 리턴값 확인
          print(f'Main got: {result}')
     except Exception as e:
          print(f'Failed with: {e}')


     # with 를 사용하지 않았음으로 명시적으로 닫아줘야 함.
     pool.close()
    

 

ThreadPoolExecutor 의 코드 예

import concurrent.futures


def task(value):
      # ...
      return value


if __name__ == '__main__':
       # 쓰레드 수를 지정하는 경우 -> concurrent.futures. ThreadPoolExecutor(max_workers=10)
       with concurrent.futures.ThreadPoolExecutor() as exe:

              # (1) submit 을 이용한 태스크의 발행 
              # 혹은 future = exe.submit(task, arg1, arg2, ...) 의 형태로 단수 실행도 가능
              futures = [exe.submit(task, i) for i in range(50)]

              # 블록되지 않기 때문 여기에 다른 실행코드를 넣어도 됨

              # 모든 태스크의 종료를 기다림 
              # 단수 쓰레드의 경우 result = future.result(timeout=5) 로 결과를 받음.
              # 결과는 발행순서 관계없이 빨리 끝나는 대로 리턴

              for future in concurrent.futures.as_completed(futures):
                   print(f'>got {future.result}')
 
             # 발생순서대로 결과값을 받고 싶을 경우는
             # for future in futures:
             #      print(future.result())
         
              # (2) (1)과 같은 작업이지만 map 을 이용하여 파라미터만 바뀌는 반복적인 태스크의 발행
              for result in exe.map(task, range(50)):
                   # 실행이 끝나기를 기다리고 결과를 받음 
                   print(f'>got {result}')

              # report that all tasks are completed
                   print('Done')

 

'data science > python' 카테고리의 다른 글

Redis Pub/Sub  (1) 2024.09.09
threading / multiprocessing / asyncio  (0) 2024.05.02
image byte 데이터 <-> numpy string  (0) 2024.04.29
two list -> dict  (0) 2024.02.27
문자열 리스트 조작  (0) 2024.02.02