# `concurrent.futures` — 병렬 작업 실행하기

버전 3.2에 추가.

소스 코드: Lib/concurrent/futures/thread.py와 Lib/concurrent/futures/process.py

`concurrent.futures` 모듈은 비동기적으로 콜러블을 실행하는 고수준 인터페이스를 제공합니다.

비동기 실행은 (`ThreadPoolExecutor`를 사용해서) 스레드나 (`ProcessPoolExecutor`를 사용해서) 별도의 프로세스로 수행 할 수 있습니다. 둘 다 추상 `Executor` 클래스로 정의된 것과 같은 인터페이스를 구현합니다.

## Executor 객체

class `concurrent.futures.``Executor`

  비동기적으로 호출을 실행하는 메서드를 제공하는 추상 클래스입니다. 직접 사용해서는 안 되며, 구체적인 하위 클래스를 통해 사용해야 합니다.

  > `submit`(fn, /, *args, **kwargs)

  >   Schedules the callable, fn, to be executed as `fn(*args, **kwargs)` and returns a `Future` object representing the execution of the callable.

  >       with ThreadPoolExecutor(max_workers=1) as executor:
  >           future = executor.submit(pow, 323, 1235)
  >           print(future.result())

  > `map`(func, *iterables, timeout=None, chunksize=1)

  >   `map(func, *iterables)` 과 비슷하지만, 다음과 같은 차이가 있습니다:

  >   - iterables 는 느긋하게 처리되는 것이 아니라 즉시 수집됩니다.

  >   - func 는 비동기적으로 실행되며 func 에 대한 여러 호출이 동시에 이루어질 수 있습니다.

  >   반환된 이터레이터는 `__next__()` 가 호출되었을 때, `Executor.map()` 에 대한 최초 호출에서 timeout 초 후에도 결과를 사용할 수 없는 경우 `concurrent.futures.TimeoutError` 를 발생시킵니다. timeout 은 int 또는 float가 될 수 있습니다. timeout 이 지정되지 않았거나 `None` 인 경우, 대기 시간에는 제한이 없습니다.

  >   func 호출이 예외를 일으키면, 값을 이터레이터에서 꺼낼 때 해당 예외가 발생합니다.

  >   `ProcessPoolExecutor`를 사용할 때, 이 메서드는 iterables 를 다수의 덩어리로 잘라서 별도의 작업으로 풀에 제출합니다. 이러한 덩어리의 (대략적인) 크기는 chunksize 를 양의 정수로 설정하여 지정할 수 있습니다. 매우 긴 이터러블의 경우 chunksize 에 큰 값을 사용하면 기본 크기인 1에 비해 성능이 크게 향상될 수 있습니다. `ThreadPoolExecutor` 의 경우, chunksize 는 아무런 효과가 없습니다.

  >   버전 3.5에서 변경: chunksize 인자가 추가되었습니다.

  > `shutdown`(wait=True, *, cancel_futures=False)

  >   현재 계류 중인 퓨처가 실행 완료될 때, 사용 중인 모든 자원을 해제해야 한다는 것을 실행기에 알립니다. 종료(shutdown) 후에 이루어지는 `Executor.submit()` 과 `Executor.map()` 호출은 `RuntimeError` 를 발생시킵니다.

  >   wait 가 `True` 면, 계류 중인 모든 퓨처가 실행을 마치고 실행기와 관련된 자원이 해제될 때까지 이 메서드는 돌아오지 않습니다. wait 가 `False` 면, 이 메서드는 즉시 돌아오고 실행기와 연관된 자원은 계류 중인 모든 퓨처가 실행을 마칠 때 해제됩니다. wait 의 값과 관계없이, 모든 계류 중인 퓨처가 실행을 마칠 때까지 전체 파이썬 프로그램이 종료되지 않습니다.

  >   cancel_futures가 `True`이면, 이 메서드는 실행기가 실행을 시작시키지 않은 계류 중인 모든 퓨처를 취소합니다. cancel_futures의 값과 관계없이 완료되었거나 실행 중인 퓨처는 취소되지 않습니다.

  >   cancel_futures와 wait가 모두 `True`이면, 이 메서드가 반환하기 전에 실행기가 실행을 시작한 모든 퓨처가 완료됩니다. 나머지 퓨처는 취소됩니다.

  >   `with` 문을 사용하여 `Executor`를 종료시키면 (`Executor.shutdown()` 를 wait 값 `True` 로 호출한 것처럼 대기합니다), 이 메서드를 명시적으로 호출할 필요가 없어집니다.:

  >       import shutil
  >       with ThreadPoolExecutor(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')

  >   버전 3.9에서 변경: cancel_futures를 추가했습니다.

## ThreadPoolExecutor

`ThreadPoolExecutor` 는 스레드 풀을 사용하여 호출을 비동기적으로 실행하는 `Executor` 서브 클래스입니다.

`Future`와 관련된 콜러블 객체가 다른 `Future` 의 결과를 기다릴 때 교착 상태가 발생할 수 있습니다. 예를 들면:

    import time
    def wait_on_b():
        time.sleep(5)
        print(b.result())  # b will never complete because it is waiting on a.
        return 5

    def wait_on_a():
        time.sleep(5)
        print(a.result())  # a will never complete because it is waiting on b.
        return 6

    executor = ThreadPoolExecutor(max_workers=2)
    a = executor.submit(wait_on_b)
    b = executor.submit(wait_on_a)

그리고:

    def wait_on_future():
        f = executor.submit(pow, 5, 2)
        # This will never complete because there is only one worker thread and
        # it is executing this function.
        print(f.result())

    executor = ThreadPoolExecutor(max_workers=1)
    executor.submit(wait_on_future)

class `concurrent.futures.``ThreadPoolExecutor`(max_workers=None, thread_name_prefix='', initializer=None, initargs=())

  최대 max_workers 스레드의 풀을 사용하여 호출을 비동기적으로 실행하는 `Executor` 서브 클래스.

  initializer 는 각 작업자 스레드의 시작 부분에서 호출되는 선택적 콜러블입니다; initargs 는 initializer에 전달되는 인자들의 튜플입니다. initializer 가 예외를 발생시키는 경우, 현재 계류 중인 모든 작업과 풀에 추가로 작업을 제출하려는 시도는 `BrokenThreadPool` 을 발생시킵니다.

  버전 3.5에서 변경: max_workers 가 `None` 이거나 주어지지 않았다면, 기본값으로 기계의 프로세서 수에 `5` 를 곱한 값을 사용합니다. `ThreadPoolExecutor` 가 CPU 작업보다는 I/O를 동시에 진행하는데 자주 쓰이고, 작업자의 수가 `ProcessPoolExecutor` 보다 많아야 한다고 가정하고 있습니다.

  버전 3.6에 추가: thread_name_prefix 인자가 추가되어, 디버깅 편의를 위해 사용자가 풀이 만드는 작업자 스레드의 `threading.Thread` 이름을 제어 할 수 있습니다.

  버전 3.7에서 변경: initializer 및 initargs 인자가 추가되었습니다.

  버전 3.8에서 변경: max_workers의 기본값은 `min(32, os.cpu_count() + 4)`로 변경됩니다. 이 기본값은 I/O 병목 작업을 위해 최소 5개의 작업자를 유지합니다. GIL을 반납하는 CPU 병목 작업을 위해 최대 32개의 CPU 코어를 사용합니다. 또한 많은 코어를 가진 시스템에서 매우 큰 자원을 묵시적으로 사용하는 것을 방지합니다.

  ThreadPoolExecutor는 이제 max_workers 작업자 스레드를 시작하기 전에 유휴 작업자 스레드를 재사용합니다.

### ThreadPoolExecutor 예제

    import concurrent.futures
    import urllib.request

    URLS = ['http://www.foxnews.com/',
            'http://www.cnn.com/',
            'http://europe.wsj.com/',
            'http://www.bbc.co.uk/',
            'http://nonexistant-subdomain.python.org/']

    # Retrieve a single page and report the URL and contents
    def load_url(url, timeout):
        with urllib.request.urlopen(url, timeout=timeout) as conn:
            return conn.read()

    # We can use a with statement to ensure threads are cleaned up promptly
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        # Start the load operations and mark each future with its URL
        future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
        for future in concurrent.futures.as_completed(future_to_url):
            url = future_to_url[future]
            try:
                data = future.result()
            except Exception as exc:
                print('%r generated an exception: %s' % (url, exc))
            else:
                print('%r page is %d bytes' % (url, len(data)))

## ProcessPoolExecutor

`ProcessPoolExecutor` 클래스는 프로세스 풀을 사용하여 호출을 비동기적으로 실행하는 `Executor` 서브 클래스입니다. `ProcessPoolExecutor` 는 `multiprocessing` 모듈을 사용합니다. 전역 인터프리터 록 을 피할 수 있도록 하지만, 오직 피클 가능한 객체만 실행되고 반환될 수 있음을 의미합니다.

`__main__` 모듈은 작업자 서브 프로세스가 임포트 할 수 있어야 합니다. 즉, `ProcessPoolExecutor` 는 대화형 인터프리터에서 작동하지 않습니다.

`ProcessPoolExecutor` 에 제출된 콜러블에서 `Executor` 나 `Future` 메서드를 호출하면 교착 상태가 발생합니다.

class `concurrent.futures.``ProcessPoolExecutor`(max_workers=None, mp_context=None, initializer=None, initargs=())

  최대 max_workers 프로세스의 풀을 사용하여 호출을 비동기적으로 실행하는 `Executor` 서브 클래스. max_workers 가 `None` 이거나 주어지지 않았다면, 기계의 프로세서 수를 기본값으로 사용합니다. max_workers 가 `0` 보다 작거나 같으면 `ValueError` 가 발생합니다. 윈도우에서, max_workers는 `61`보다 작거나 같아야 합니다. 그렇지 않으면 `ValueError`가 발생합니다. max_workers가 `None` 이면, 더 많은 프로세서를 사용할 수 있다 할지라도 선택된 기본값은 최대 `61`이 될 것입니다. mp_context 는 multiprocessing 컨텍스트이거나 None일 수 있습니다. 작업자들을 만드는데 사용될 것입니다. mp_context 가 `None` 이거나 주어지지 않으면 기본 multiprocessing 컨텍스트가 사용됩니다.

  initializer 는 각 작업자 프로세스의 시작 부분에서 호출되는 선택적 콜러블입니다; initargs 는 initializer에 전달되는 인자들의 튜플입니다. initializer 가 예외를 발생시키는 경우, 현재 계류 중인 모든 작업과 풀에 추가로 작업을 제출하려는 시도는 `BrokenProcessPool` 을 발생시킵니다.

  버전 3.3에서 변경: 작업자 프로세스 중 하나가 갑자기 종료되면, `BrokenProcessPool` 오류가 발생합니다. 이전에는, 동작이 정의되지 않았지만, 실행기나 그 퓨처에 대한 연산이 종종 멈추거나 교착 상태에 빠졌습니다.

  버전 3.7에서 변경: mp_context 인자가 추가되어 사용자가 풀에서 만드는 작업자 프로세스의 시작 방법을 제어 할 수 있습니다.

  initializer 및 initargs 인자가 추가되었습니다.

### ProcessPoolExecutor 예제

    import concurrent.futures
    import math

    PRIMES = [
        112272535095293,
        112582705942171,
        112272535095293,
        115280095190773,
        115797848077099,
        1099726899285419]

    def is_prime(n):
        if n < 2:
            return False
        if n == 2:
            return True
        if n % 2 == 0:
            return False

        sqrt_n = int(math.floor(math.sqrt(n)))
        for i in range(3, sqrt_n + 1, 2):
            if n % i == 0:
                return False
        return True

    def main():
        with concurrent.futures.ProcessPoolExecutor() as executor:
            for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
                print('%d is prime: %s' % (number, prime))

    if __name__ == '__main__':
        main()

## Future 객체

`Future` 클래스는 콜러블 객체의 비동기 실행을 캡슐화합니다. `Future` 인스턴스는 `Executor.submit()` 에 의해 생성됩니다.

class `concurrent.futures.``Future`

  콜러블 객체의 비동기 실행을 캡슐화합니다. `Future` 인스턴스는 `Executor.submit()` 에 의해 생성되며 테스트를 제외하고는 직접 생성되어서는 안 됩니다.

  > `cancel`()

  >   호출을 취소하려고 시도합니다. 호출이 현재 실행 중이거나 실행 종료했고 취소할 수 없는 경우 메서드는 `False` 를 반환하고, 그렇지 않으면 호출이 취소되고 메서드는 `True` 를 반환합니다.

  > `cancelled`()

  >   호출이 성공적으로 취소되었으면 `True` 를 반환합니다.

  > `running`()

  >   호출이 현재 실행 중이고 취소할 수 없는 경우 `True` 를 반환합니다.

  > `done`()

  >   호출이 성공적으로 취소되었거나 실행이 완료되었으면 `True` 를 반환합니다.

  > `result`(timeout=None)

  >   호출이 반환한 값을 돌려줍니다. 호출이 아직 완료되지 않는 경우, 이 메서드는 timeout 초까지 대기합니다. timeout 초 내에 호출이 완료되지 않으면 `concurrent.futures.TimeoutError` 가 발생합니다. timeout 은 int 또는 float가 될 수 있습니다. timeout 이 지정되지 않았거나 `None` 인 경우, 대기 시간에는 제한이 없습니다.

  >   완료하기 전에 퓨처가 취소되면 `CancelledError` 가 발생합니다.

  >   If the call raised an exception, this method will raise the same exception.

  > `exception`(timeout=None)

  >   호출이 일으킨 예외를 돌려줍니다. 호출이 아직 완료되지 않는 경우, 이 메서드는 timeout 초까지 대기합니다. timeout 초 내에 호출이 완료되지 않으면 `concurrent.futures.TimeoutError` 가 발생합니다. timeout 은 int 또는 float가 될 수 있습니다. timeout 이 지정되지 않았거나 `None` 인 경우, 대기 시간에는 제한이 없습니다.

  >   완료하기 전에 퓨처가 취소되면 `CancelledError` 가 발생합니다.

  >   호출이 예외 없이 완료되면, `None` 이 반환됩니다.

  > `add_done_callback`(fn)

  >   콜러블 fn 을 퓨처에 연결합니다. fn 은 퓨처가 취소되거나 실행이 종료될 때 퓨처를 유일한 인자로 호출됩니다.

  >   추가된 콜러블은 추가된 순서대로 호출되며, 항상 콜러블을 추가한 프로세스에 속하는 스레드에서 호출됩니다. 콜러블이 `Exception` 서브 클래스를 발생시키면, 로그 되고 무시됩니다. 콜러블이 `BaseException` 서브 클래스를 발생시키면, 동작은 정의되지 않습니다.

  >   퓨처가 이미 완료되었거나 취소된 경우 fn 이 즉시 호출됩니다.

  다음 `Future` 메서드는 단위 테스트와 `Executor` 의 구현을 위한 것입니다.

  > `set_running_or_notify_cancel`()

  >   이 메서드는 `Future`와 관련된 작업을 실행하기 전에 `Executor` 구현에 의해서만 호출되거나 단위 테스트에서만 호출되어야 합니다.

  >   메서드가 `False` 를 반환하면, `Future` 가 취소된 것입니다. 즉 `Future.cancel()` 이 호출되었고 True를 반환했습니다. `Future` 완료를 기다리는 (즉, `as_completed()` 또는 `wait()`를 통해) 모든 스레드는 깨어납니다.

  >   메서드가 `True` 를 반환하면, `Future` 가 취소되지 않았고 실행 상태로 진입했습니다. 즉 `Future.running()` 을 호출하면 True 가 반환됩니다.

  >   이 메서드는 한 번만 호출 할 수 있으며, `Future.set_result()` 또는 `Future.set_exception()` 이 호출 된 후에는 호출할 수 없습니다.

  > `set_result`(result)

  >   `Future`와 관련된 작업 결과를 result 로 설정합니다.

  >   이 메서드는 `Executor` 구현과 단위 테스트에서만 사용해야 합니다.

  >   버전 3.8에서 변경: 이 메서드는 `Future`가 이미 완료되었으면 `concurrent.futures.InvalidStateError`를 발생시킵니다.

  > `set_exception`(exception)

  >   `Future`와 관련된 작업 결과를 `Exception` exception 으로 설정합니다.

  >   이 메서드는 `Executor` 구현과 단위 테스트에서만 사용해야 합니다.

  >   버전 3.8에서 변경: 이 메서드는 `Future`가 이미 완료되었으면 `concurrent.futures.InvalidStateError`를 발생시킵니다.

## 모듈 함수

`concurrent.futures.``wait`(fs, timeout=None, return_when=ALL_COMPLETED)

  Wait for the `Future` instances (possibly created by different `Executor` instances) given by fs to complete. Duplicate futures given to fs are removed and will be returned only once. Returns a named 2-tuple of sets. The first set, named `done`, contains the futures that completed (finished or cancelled futures) before the wait completed. The second set, named `not_done`, contains the futures that did not complete (pending or running futures).

  timeout 은 반환하기 전에 대기 할 최대 시간(초)을 제어하는 데 사용할 수 있습니다. timeout 은 int 또는 float가 될 수 있습니다. timeout 이 지정되지 않았거나 `None` 인 경우, 대기 시간에는 제한이 없습니다.

  return_when 은, 이 함수가 언제 반환되어야 하는지를 나타냅니다. 다음 상수 중 하나여야 합니다:

  상수 | 설명
  --- | ---
  `FIRST_COMPLETED` | 퓨처가 어느 하나라도 끝나거나 취소될 때 함수가 반환됩니다.
  `FIRST_EXCEPTION` | 어느 한 퓨처가 예외를 일으켜 완료하면 함수가 반환됩니다. 어떤 퓨처도 예외를 발생시키지 않으면 `ALL_COMPLETED`와 같습니다.
  `ALL_COMPLETED` | 모든 퓨처가 끝나거나 취소되면 함수가 반환됩니다.

`concurrent.futures.``as_completed`(fs, timeout=None)

  fs 로 주어진 여러 (서로 다른 `Executor` 인스턴스가 만든 것들도 가능합니다) 퓨처들이 완료되는 대로 (끝났거나 취소된 퓨처) 일드 하는 `Future` 인스턴스의 이터레이터를 반환합니다. fs 에 중복된 퓨처가 들어있으면 한 번만 반환됩니다. `as_completed()` 가 호출되기 전에 완료한 모든 퓨처들이 먼저 일드 됩니다. 반환된 이터레이터는, `__next__()` 가 호출되고, `as_completed()` 호출 시점으로부터 timeout 초 후에 결과를 얻을 수 없는 경우 `concurrent.futures.TimeoutError` 를 발생시킵니다. timeout 은 int 또는 float가 될 수 있습니다. timeout 이 지정되지 않았거나 `None` 인 경우, 대기 시간에는 제한이 없습니다.

더 보기

PEP 3148 – 퓨처 - 계산을 비동기적으로 실행

  파이썬 표준 라이브러리에 포함하기 위해, 이 기능을 설명한 제안.

## 예외 클래스

exception `concurrent.futures.``CancelledError`

  퓨처가 취소될 때 발생합니다.

exception `concurrent.futures.``TimeoutError`

  퓨처 연산이 지정된 시간제한을 초과할 때 발생합니다.

exception `concurrent.futures.``BrokenExecutor`

  `RuntimeError` 에서 파생됩니다, 이 예외 클래스는 어떤 이유로 실행기가 망가져서 새 작업을 제출하거나 실행할 수 없을 때 발생합니다.

  버전 3.7에 추가.

exception `concurrent.futures.``InvalidStateError`

  퓨처에 현재 상태에서 허용되지 않는 연산이 수행될 때 발생합니다.

  버전 3.8에 추가.

exception `concurrent.futures.thread.``BrokenThreadPool`

  `BrokenExecutor` 에서 파생됩니다, 이 예외 클래스는 `ThreadPoolExecutor` 의 작업자 중 하나가 초기화에 실패했을 때 발생합니다.

  버전 3.7에 추가.

exception `concurrent.futures.process.``BrokenProcessPool`

  `BrokenExecutor` 에서 파생됩니다 (예전에는 `RuntimeError`), 이 예외 클래스는 `ProcessPoolExecutor` 의 작업자 중 하나가 깨끗하지 못한 방식으로 (예를 들어, 외부에서 강제 종료된 경우) 종료되었을 때 발생합니다.

  버전 3.3에 추가.