GIL(Global internal lock)로 인한 제약
- python에서는 항상 한 번에 하나의 스레드만 실행됨
- 두 개 이상의 스레드가 동시에 실행될 때 두 개 이상의 스레드가 하나의 자원을 동시에 액세스할 때 발생할 수 있는 문제점을 방지하기 위함
- 이 때문에 멀티스레드를 통한 분산처리는 파이썬 내에서 의미가 없음
- 이 때문에 분산처리를 하려면 멀티스레딩을 해야 하는데, 이 또한 병목이 되는 일
Concurrent.futures 모듈
- 별도 규격의 스레드 객체를 작성하지 않고 함수 호출을 객체화
- 이를 통해 다른 프로세스에서 실행할 수 있게 해줌
- Executer 클래스가 핵심 :
- ThreadPoolExecutor
: 멀티스레딩
ProcessPoolExector
: 멀티프로세싱
Future
- 처리되는 실행 단위들의 완료 여부를 확인할 수 있는 객체
- 확인 후 개체 내 작업이 완료되기를 기다리거나, 미리 콜백을 넘겨놓아둘 수 있음
- 호출해야 할 함수와 그에 전달될 인자들을 executor에 넘겨주는 것으로 시작
- 이 때 해당 메소드를 바로 리턴하게 되는데 이것이 future 객체
Executor
- 초기화 시에 몇 개의 worker가 사용될 것인지를 정해주면 전달되는 작업들을 큐에 넣음
- worker pool에서 사용 가능한 worker로 작업을 처리
submit(fn, *args, **kwargs)
- 함수 fn 에 대해 주어진 인자들을 전달하여 실행할 수 있는
Future
객체를 리턴
- 해당 함수는 호출 즉시 스케쥴링
with ThreadPoolExcutor(max_workers=1) as executor:
future = executor.submit(pow, 323, 1235)
print(future.result())
map(func, *iterables, timeout=None)
- 일반함수 map과 동일하지만, 각 호출은 병렬적으로 일어남
- 만약 timeout 값이 지정된 경우, 기간 내에 완료되지 않은 호출이 있으면
TimeoutError
가 일어남
- 입력데이터와 동작함수를 짝지어서 바로 스케줄링하도록 함
map
함수는 이터레이터를 리턴하는데, 이는 각 개별 작업이 도잇에 실행된 후 먼저 종료된 작업부터 내놓는 리턴값을 내놓게 됨
shutdown(wait=True)
- executor 에 종료 시그널을 보냄
- 시그널을 받은 executor는 모든 future에 대해 리소스 정리
- 강제 shutdown을 피하기 위해 with 구문 내에서 사용
import shutil
with ThreadPoolExcutor(max_workers=4) as e:
e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
e.submit(shutil.copy, 'src4.txt', 'dest4.txt')
Reference