Python 다중 스레드와 병렬 처리
Python에서 다중 스레드와 병렬 처리는 고성능 애플리케이션을 개발할 때 매우 중요한 주제입니다. 이번 포스팅에서는 다중 스레드와 병렬 처리의 개념을 살펴보고, Python에서 이를 구현하는 방법을 다양한 예제와 함께 설명하겠습니다.
다중 스레드란?
다중 스레드는 하나의 프로세스 내에서 여러 실행 단위를 동시에 수행하는 기술입니다. 각 스레드는 독립적으로 실행되며, 같은 메모리 공간을 공유합니다. Python에서는 threading
모듈을 사용하여 다중 스레드를 구현할 수 있습니다.
다중 스레드 기본 예제
import threading
import time
def print_numbers():
for i in range(1, 6):
print(f"Number: {i}")
time.sleep(1)
def print_letters():
for letter in ['A', 'B', 'C', 'D', 'E']:
print(f"Letter: {letter}")
time.sleep(1)
# 스레드 생성
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)
# 스레드 시작
thread1.start()
thread2.start()
# 스레드가 끝날 때까지 대기
thread1.join()
thread2.join()
print("모든 작업이 완료되었습니다.")
실행 결과 예시
Number: 1
Letter: A
Number: 2
Letter: B
...
모든 작업이 완료되었습니다.
GIL(Global Interpreter Lock)
Python의 기본 구현인 CPython은 GIL(Global Interpreter Lock)이라는 제약이 있습니다. GIL은 한 번에 하나의 스레드만 Python 바이트코드를 실행할 수 있도록 제한합니다. 따라서 다중 스레드를 사용해도 CPU 바운드 작업에서는 성능이 크게 향상되지 않을 수 있습니다.
이를 해결하기 위해 다음과 같은 방법을 고려할 수 있습니다:
- 멀티프로세싱 사용
- I/O 바운드 작업에서 스레딩 활용
멀티프로세싱
멀티프로세싱은 프로세스를 여러 개 생성하여 병렬로 실행하는 방식입니다. Python의 multiprocessing
모듈을 사용하면 GIL의 제약을 피할 수 있습니다.
멀티프로세싱 기본 예제
from multiprocessing import Process
import os
def worker(task_id):
print(f"Task {task_id} is running on process {os.getpid()}")
if __name__ == "__main__":
processes = []
for i in range(4):
process = Process(target=worker, args=(i,))
processes.append(process)
process.start()
for process in processes:
process.join()
print("모든 프로세스가 완료되었습니다.")
실행 결과 예시
Task 0 is running on process 12345
Task 1 is running on process 12346
...
모든 프로세스가 완료되었습니다.
동시성과 병렬성
- 동시성: 여러 작업을 번갈아 수행하며 진행하는 방식입니다. (예: 스레딩)
- 병렬성: 여러 작업을 동시에 수행하는 방식입니다. (예: 멀티프로세싱)
ThreadPoolExecutor와 ProcessPoolExecutor
Python에서는 concurrent.futures
모듈의 ThreadPoolExecutor
와 ProcessPoolExecutor
를 사용하여 더 간단하게 스레드와 프로세스를 관리할 수 있습니다.
ThreadPoolExecutor 예제
from concurrent.futures import ThreadPoolExecutor
import time
def task(name):
print(f"Task {name} 시작")
time.sleep(2)
print(f"Task {name} 완료")
with ThreadPoolExecutor(max_workers=3) as executor:
tasks = [executor.submit(task, i) for i in range(5)]
ProcessPoolExecutor 예제
from concurrent.futures import ProcessPoolExecutor
import os
def compute_square(n):
print(f"Process {os.getpid()} 계산 중: {n}^2")
return n * n
with ProcessPoolExecutor(max_workers=4) as executor:
results = list(executor.map(compute_square, range(1, 6)))
print("결과:", results)
언제 스레드와 프로세스를 사용할까?
스레드:
- I/O 바운드 작업 (파일 읽기/쓰기, 네트워크 요청 등)
- GIL의 영향을 받지 않는 작업
프로세스:
- CPU 바운드 작업 (복잡한 계산 등)
- GIL의 영향을 받는 작업
주의사항
- 데드락: 스레드 간 자원을 잘못 관리하면 데드락이 발생할 수 있습니다.
- 오버헤드: 많은 프로세스를 생성하면 오버헤드가 증가할 수 있습니다.
- 디버깅: 멀티스레드/멀티프로세싱 환경에서는 디버깅이 어려울 수 있습니다.
이제 여러분은 Python에서 다중 스레드와 병렬 처리를 다룰 준비가 되었습니다! 위의 예제를 직접 실행해보고, 여러분의 프로젝트에 적합한 방식을 선택해 보세요.
'Python > Python 심화' 카테고리의 다른 글
Python collections 모듈을 이용한 데이터 구조 (0) | 2025.07.23 |
---|---|
Python `datetime`과 시간 관리 (0) | 2025.07.22 |
Python 예외 처리 및 예외 클래스 생성 (0) | 2025.07.20 |
Python 메타클래스 이해하기 (0) | 2025.07.19 |
Python 데코레이터와 제너레이터 (0) | 2025.07.18 |