concurrent.futures — Launching parallel tasks

Added in version 3.2.

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


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

The asynchronous execution can be performed with threads, using ThreadPoolExecutor or InterpreterPoolExecutor, or separate processes, using ProcessPoolExecutor. Each implements the same interface, which is defined by the abstract Executor class.

Availability: not WASI.

This module does not work or is not available on WebAssembly. See WebAssembly platforms for more information.

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(fn, *iterables, timeout=None, chunksize=1)

Similar to map(fn, *iterables) except:

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

  • fn is executed asynchronously and several calls to fn may be made concurrently.

The returned iterator raises a TimeoutError if __next__() is called and the result isn’t available after timeout seconds from the original call to Executor.map(). timeout can be an int or a float. If timeout is not specified or None, there is no limit to the wait time.

If a fn call raises an exception, then that exception will be raised when its value is retrieved from the iterator.

When using ProcessPoolExecutor, this method chops iterables into a number of chunks which it submits to the pool as separate tasks. The (approximate) size of these chunks can be specified by setting chunksize to a positive integer. For very long iterables, using a large value for chunksize can significantly improve performance compared to the default size of 1. With ThreadPoolExecutor and InterpreterPoolExecutor, chunksize has no effect.

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

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

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

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

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

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

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

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 서브 클래스.

All threads enqueued to ThreadPoolExecutor will be joined before the interpreter can exit. Note that the exit handler which does this is executed before any exit handlers added using atexit. This means exceptions in the main thread must be caught and handled in order to signal threads to exit gracefully. For this reason, it is recommended that ThreadPoolExecutor not be used for long-running tasks.

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

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

버전 3.6에서 변경: Added the thread_name_prefix parameter to allow users to control the threading.Thread names for worker threads created by the pool for easier debugging.

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

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

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

버전 3.13에서 변경: Default value of max_workers is changed to min(32, (os.process_cpu_count() or 1) + 4).

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://nonexistent-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)))

InterpreterPoolExecutor

The InterpreterPoolExecutor class uses a pool of interpreters to execute calls asynchronously. It is a ThreadPoolExecutor subclass, which means each worker is running in its own thread. The difference here is that each worker has its own interpreter, and runs each task using that interpreter.

The biggest benefit to using interpreters instead of only threads is true multi-core parallelism. Each interpreter has its own Global Interpreter Lock, so code running in one interpreter can run on one CPU core, while code in another interpreter runs unblocked on a different core.

The tradeoff is that writing concurrent code for use with multiple interpreters can take extra effort. However, this is because it forces you to be deliberate about how and when interpreters interact, and to be explicit about what data is shared between interpreters. This results in several benefits that help balance the extra effort, including true multi-core parallelism, For example, code written this way can make it easier to reason about concurrency. Another major benefit is that you don’t have to deal with several of the big pain points of using threads, like race conditions.

Each worker’s interpreter is isolated from all the other interpreters. “Isolated” means each interpreter has its own runtime state and operates completely independently. For example, if you redirect sys.stdout in one interpreter, it will not be automatically redirected any other interpreter. If you import a module in one interpreter, it is not automatically imported in any other. You would need to import the module separately in interpreter where you need it. In fact, each module imported in an interpreter is a completely separate object from the same module in a different interpreter, including sys, builtins, and even __main__.

Isolation means a mutable object, or other data, cannot be used by more than one interpreter at the same time. That effectively means interpreters cannot actually share such objects or data. Instead, each interpreter must have its own copy, and you will have to synchronize any changes between the copies manually. Immutable objects and data, like the builtin singletons, strings, and tuples of immutable objects, don’t have these limitations.

Communicating and synchronizing between interpreters is most effectively done using dedicated tools, like those proposed in PEP 734. One less efficient alternative is to serialize with pickle and then send the bytes over a shared socket or pipe.

class concurrent.futures.InterpreterPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=(), shared=None)

A ThreadPoolExecutor subclass that executes calls asynchronously using a pool of at most max_workers threads. Each thread runs tasks in its own interpreter. The worker interpreters are isolated from each other, which means each has its own runtime state and that they can’t share any mutable objects or other data. Each interpreter has its own Global Interpreter Lock, which means code run with this executor has true multi-core parallelism.

The optional initializer and initargs arguments have the same meaning as for ThreadPoolExecutor: the initializer is run when each worker is created, though in this case it is run.in the worker’s interpreter. The executor serializes the initializer and initargs using pickle when sending them to the worker’s interpreter.

참고

Functions defined in the __main__ module cannot be pickled and thus cannot be used.

참고

The executor may replace uncaught exceptions from initializer with ExecutionFailed.

The optional shared argument is a dict of objects that all interpreters in the pool share. The shared items are added to each interpreter’s __main__ module. Not all objects are shareable. Shareable objects include the builtin singletons, str and bytes, and memoryview. See PEP 734 for more info.

Other caveats from parent ThreadPoolExecutor apply here.

submit() and map() work like normal, except the worker serializes the callable and arguments using pickle when sending them to its interpreter. The worker likewise serializes the return value when sending it back.

참고

Functions defined in the __main__ module cannot be pickled and thus cannot be used.

When a worker’s current task raises an uncaught exception, the worker always tries to preserve the exception as-is. If that is successful then it also sets the __cause__ to a corresponding ExecutionFailed instance, which contains a summary of the original exception. In the uncommon case that the worker is not able to preserve the original as-is then it directly preserves the corresponding ExecutionFailed instance instead.

ProcessPoolExecutor

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

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

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

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

An Executor subclass that executes calls asynchronously using a pool of at most max_workers processes. If max_workers is None or not given, it will default to os.process_cpu_count(). If max_workers is less than or equal to 0, then a ValueError will be raised. On Windows, max_workers must be less than or equal to 61. If it is not then ValueError will be raised. If max_workers is None, then the default chosen will be at most 61, even if more processors are available. mp_context can be a multiprocessing context or None. It will be used to launch the workers. If mp_context is None or not given, the default multiprocessing context is used. See 컨텍스트 및 시작 방법.

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

max_tasks_per_child is an optional argument that specifies the maximum number of tasks a single process can execute before it will exit and be replaced with a fresh worker process. By default max_tasks_per_child is None which means worker processes will live as long as the pool. When a max is specified, the “spawn” multiprocessing start method will be used by default in absence of a mp_context parameter. This feature is incompatible with the “fork” start method.

버전 3.3에서 변경: When one of the worker processes terminates abruptly, a BrokenProcessPool error is now raised. Previously, behaviour was undefined but operations on the executor or its futures would often freeze or deadlock.

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

initializerinitargs 인자가 추가되었습니다.

버전 3.11에서 변경: The max_tasks_per_child argument was added to allow users to control the lifetime of workers in the pool.

버전 3.12에서 변경: On POSIX systems, if your application has multiple threads and the multiprocessing context uses the "fork" start method: The os.fork() function called internally to spawn workers may raise a DeprecationWarning. Pass a mp_context configured to use a different start method. See the os.fork() documentation for further explanation.

버전 3.13에서 변경: max_workers uses os.process_cpu_count() by default, instead of os.cpu_count().

버전 3.14에서 변경: The default process start method (see 컨텍스트 및 시작 방법) changed away from fork. If you require the fork start method for ProcessPoolExecutor you must explicitly pass mp_context=multiprocessing.get_context("fork").

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)

Return the value returned by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then a TimeoutError will be raised. timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time.

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

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

exception(timeout=None)

Return the exception raised by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then a TimeoutError will be raised. timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time.

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

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

add_done_callback(fn)

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

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

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

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

set_running_or_notify_cancel()

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

If the method returns False then the Future was cancelled, i.e. Future.cancel() was called and returned True. Any threads waiting on the Future completing (i.e. through as_completed() or wait()) will be woken up.

If the method returns True then the Future was not cancelled and has been put in the running state, i.e. calls to Future.running() will return 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 은, 이 함수가 언제 반환되어야 하는지를 나타냅니다. 다음 상수 중 하나여야 합니다:

상수

설명

concurrent.futures.FIRST_COMPLETED

퓨처가 어느 하나라도 끝나거나 취소될 때 함수가 반환됩니다.

concurrent.futures.FIRST_EXCEPTION

The function will return when any future finishes by raising an exception. If no future raises an exception then it is equivalent to ALL_COMPLETED.

concurrent.futures.ALL_COMPLETED

모든 퓨처가 끝나거나 취소되면 함수가 반환됩니다.

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

Returns an iterator over the Future instances (possibly created by different Executor instances) given by fs that yields futures as they complete (finished or cancelled futures). Any futures given by fs that are duplicated will be returned once. Any futures that completed before as_completed() is called will be yielded first. The returned iterator raises a TimeoutError if __next__() is called and the result isn’t available after timeout seconds from the original call to as_completed(). timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time.

더 보기

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

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

예외 클래스

exception concurrent.futures.CancelledError

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

exception concurrent.futures.TimeoutError

A deprecated alias of TimeoutError, raised when a future operation exceeds the given timeout.

버전 3.11에서 변경: This class was made an alias of TimeoutError.

exception concurrent.futures.BrokenExecutor

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

Added in version 3.7.

exception concurrent.futures.InvalidStateError

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

Added in version 3.8.

exception concurrent.futures.thread.BrokenThreadPool

Derived from BrokenExecutor, this exception class is raised when one of the workers of a ThreadPoolExecutor has failed initializing.

Added in version 3.7.

exception concurrent.futures.interpreter.BrokenInterpreterPool

Derived from BrokenThreadPool, this exception class is raised when one of the workers of a InterpreterPoolExecutor has failed initializing.

Added in version 3.14.

exception concurrent.futures.interpreter.ExecutionFailed

Raised from InterpreterPoolExecutor when the given initializer fails or from submit() when there’s an uncaught exception from the submitted task.

Added in version 3.14.

exception concurrent.futures.process.BrokenProcessPool

Derived from BrokenExecutor (formerly RuntimeError), this exception class is raised when one of the workers of a ProcessPoolExecutor has terminated in a non-clean fashion (for example, if it was killed from the outside).

Added in version 3.3.