파이썬 - 스레드 풀

안녕하세요, 파이썬 프로그래머를 꿈꾸는 여러분! 오늘, 우리는 흥미로운 스레드 풀의 세계에 뛰어들어보겠습니다. 여러분의 친절한 이웃 컴퓨터 교사로서, 저는 여러분을 이 여정을 단계별로 안내할 수 있습니다. 프로그래밍에 새로운 사람이라도 걱정하지 마세요; 우리는 기본부터 시작하여 점차 고급으로 진행하겠습니다. 그럼, 좋아하는 음료수를 준비하고 편안하게 앉아서 여러분의 모험을 시작해보세요!

Python - Thread Pools

스레드 풀이란 무엇인가요?

코드에 뛰어들기 전에, 스레드 풀이란 무엇인지와 왜 중요한지 이해해보겠습니다. 바쁜 레스토랑을 운영하는 것을 상상해보세요. 고객이 들어오면 매번 새 직원을 고용하는 대신, 서빙할 준비가 된 웨이터 팀이 있다고 생각해보세요. 이 팀이 여러분의 "풀"입니다. 프로그래밍에서는 스레드 풀도 비슷합니다 - 필요할 때 작업을 할 수 있는 재사용 가능한 스레드의 그룹입니다.

스레드 풀은 새로운 스레드를 만들어야 하는 오버헤드 없이 여러 작업을 효율적으로 관리하는 데 도움이 됩니다. 특히 여러 짧은 수명의 작업이 동시에 실행되어야 하는 경우에 매우 유용합니다.

이제, 파이썬에서 스레드 풀을 구현하는 두 가지 주요 방법을 탐구해보겠습니다: ThreadPool 클래스와 ThreadPoolExecutor 클래스입니다.

Python ThreadPool 클래스 사용

ThreadPool 클래스는 multiprocessing.pool 모듈의 일부입니다. 약간 오래된 것이지만 여전히 널리 사용됩니다. 이를 어떻게 사용할 수 있는지 살펴보겠습니다:

from multiprocessing.pool import ThreadPool
import time

def worker(num):
print(f"Worker {num} is starting")
time.sleep(2)  # 작업 시뮬레이션
print(f"Worker {num} is done")
return num * 2

# 3개의 작업 스레드로 스레드 풀 생성
pool = ThreadPool(3)

# 풀에 5개의 작업 제출
results = pool.map(worker, range(5))

# 풀 닫고 모든 작업 완료를 기다림
pool.close()
pool.join()

print("All workers have finished")
print(f"Results: {results}")

이를 분석해보겠습니다:

  1. ThreadPooltime을 임포트합니다 (시뮬레이션 작업용).
  2. 작업을 시뮬레이션하고 값을 반환하는 worker 함수를 정의합니다.
  3. 3개의 작업 스레드로 ThreadPool을 생성합니다.
  4. pool.map()을 사용하여 풀에 5개의 작업을 제출합니다. 이는 사용 가능한 스레드에 작업을 분산시킵니다.
  5. 풀을 닫고 모든 작업이 완료될 때까지 기다립니다.
  6. 마지막으로 결과를 인쇄합니다.

이를 실행하면 5개의 작업이 있지만, 3개의 작업 스레드에 의해 실행된다는 것을 볼 수 있습니다. 이를 통해 스레드 풀이 어떻게 작업을 관리하는지 보여줍니다.

Python ThreadPoolExecutor 클래스 사용

이제 더 현대적인 ThreadPoolExecutor 클래스를 concurrent.futures 모듈에서 살펴보겠습니다. 이 클래스는 콜러블을 비동기적으로 실행하는 더 높은 수준의 인터페이스를 제공합니다.

from concurrent.futures import ThreadPoolExecutor
import time

def worker(num):
print(f"Worker {num} is starting")
time.sleep(2)  # 작업 시뮬레이션
print(f"Worker {num} is done")
return num * 2

# 3개의 작업 스레드로 ThreadPoolExecutor 생성
with ThreadPoolExecutor(max_workers=3) as executor:
# 실행자에게 5개의 작업 제출
futures = [executor.submit(worker, i) for i in range(5)]

# 모든 작업 완료를 기다리고 결과 가져오기
results = [future.result() for future in futures]

print("All workers have finished")
print(f"Results: {results}")

이 예제를 분석해보겠습니다:

  1. ThreadPoolExecutorThreadPool 대신 임포트합니다.
  2. with 문을 사용하여 실행자를 생성하고 관리합니다. 이는 완료 후 올바른 정리를 보장합니다.
  3. executor.submit()을 사용하여 개별 작업을 풀에 제출합니다.
  4. 작업의 최종 결과를 나타내는 Future 객체의 목록을 만듭니다.
  5. 각 작업의 결과를 기다리고 가져오기 위해 future.result()를 사용합니다.

ThreadPoolExecutor는 더 많은 유연성을 제공하며, 특히 더 복잡한 시나리오에서는 더 쉽게 사용할 수 있습니다.

ThreadPool와 ThreadPoolExecutor 비교

이 두 접근 방식을 비교해보겠습니다:

기능 ThreadPool ThreadPoolExecutor
모듈 multiprocessing.pool concurrent.futures
파이썬 버전 모든 버전 3.2 이상
컨텍스트 관리자 아니요
유연성 적음 많음
오류 처리 기본 고급
취소 제한적 지원
Future 객체 아니요

보시다시피, ThreadPoolExecutor는 더 많은 기능을 제공하며 일반적으로 더 유연합니다. 그러나 ThreadPool도 여전히 유용하며, 특히 오래된 파이썬 버전에서 작업하거나 기존 코드와의 호환성을 유지해야 하는 경우에는 유용합니다.

최상의 실천과 팁

  1. 적절한 수의 스레드 선택: 너무 적은 스레드는 CPU를 완전히 활용하지 못할 수 있고, 너무 많은 스레드는 오버헤드를 초래할 수 있습니다. 좋은 시작점은 여러분의 기계의 CPU 코어 수입니다.

  2. 컨텍스트 관리자 사용: ThreadPoolExecutor를 사용할 때는 항상 with 문을 사용하여 올바른 정리를 보장하세요.

3 예외 처리: 작업 함수에서 예외를 처리하여 조용히 실패하지 않도록 주의하세요.

  1. 공유 자원에 주의: 스레드 풀을 사용할 때 공유 자원에 주의하여 경쟁 조건을 피하세요.

  2. 작업 세분화 고려: 스레드 풀은 많은 작은 작업보다는 몇 개의 큰 작업보다 더 잘 작동합니다.

결론

축하합니다! 여러분은 파이썬에서 스레드 풀에 대한 첫 걸음을 내딛었습니다. 우리는 ThreadPoolThreadPoolExecutor의 기본을 다루었으며, 이제 여러분은 자신의 프로젝트에서 이 강력한 도구를 사용할 수 있는 좋은 기반을 갖추었습니다.

레스토랑의 바쁜 주방에서 요리를 배우는 것처럼, 스레드 풀을 마스터하는 것은 연습이 필요합니다. 실험하고 실수를 할 때까지 두려워마세요 - 그게 우리가 배우는 방법입니다! 계속 코딩하고 배우며, 언제 어떻게 될지 모르겠지만, 여러분은 바쁜 주방에서 팬을 던지는 프로 셰프처럼 스레드를 주고받을 수 있을 거예요.

좋은 코딩 되세요, 여러분의 스레드는 항상 조화를 이루세요!

Credits: Image by storyset