"concurrent.futures" --- 병렬 작업 실행하기
*******************************************

Added in version 3.2.

**Source code:** Lib/concurrent/futures/thread.py,
Lib/concurrent/futures/process.py, and
Lib/concurrent/futures/interpreter.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.

가용성: not WASI.

이 모듈은 웹어셈블리에서 작동하지 않거나 제공되지 않습니다. 자세한 내
용은 웹어셈블리 플랫폼을 참조하세요.


Executor 객체
=============

class concurrent.futures.Executor

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

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

      콜러블 *fn* 이 "fn(*args, **kwargs)" 처럼 실행되도록 예약하고,
      콜러블 객체의 실행을 나타내는 "Future" 객체를 반환합니다.

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

   map(fn, *iterables, timeout=None, chunksize=1, buffersize=None)

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

      * The *iterables* are collected immediately rather than lazily,
        unless a *buffersize* is specified to limit the number of
        submitted tasks whose results have not yet been yielded. If
        the buffer is full, iteration over the *iterables* pauses
        until a result is yielded from the buffer.

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

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

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

      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에서 변경: Added the *chunksize* parameter.

      버전 3.14에서 변경: Added the *buffersize* parameter.

   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"이면, 이 메서드가 반환하
      기 전에 실행기가 실행을 시작한 모든 퓨처가 완료됩니다. 나머지 퓨
      처는 취소됩니다.

      You can avoid having to call this method explicitly if you use
      the executor as a *context manager* via the  "with" statement,
      which will shutdown the "Executor" (waiting as if
      "Executor.shutdown()" were called with *wait* set to "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 는 a 를 기다리기 때문에 완료되지 않습니다.
       return 5

   def wait_on_a():
       time.sleep(5)
       print(a.result())  # a 는 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)
       # 작업자 스레드가 하나뿐인데, wait_on_future 를 실행하고 있으므로
       # f 는 완료되지 않습니다.
       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_workers* 가 "None" 이거나 주어지지 않았다면
   , 기본값으로 기계의 프로세서 수에 "5" 를 곱한 값을 사용합니다.
   "ThreadPoolExecutor" 가 CPU 작업보다는 I/O를 동시에 진행하는데 자주
   쓰이고, 작업자의 수가 "ProcessPoolExecutor" 보다 많아야 한다고 가정
   하고 있습니다.

   버전 3.6에서 변경: 디버깅 편의를 위해 사용자가 풀이 만드는 작업자
   스레드의 "threading.Thread" 이름을 제어 할 수 있도록,
   *thread_name_prefix* 매개 변수를 추가했습니다.

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

   버전 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/']

   # 페이지 하나를 가져오고 URL 과 내용을 보고합니다
   def load_url(url, timeout):
       with urllib.request.urlopen(url, timeout=timeout) as conn:
           return conn.read()

   # with 문을 사용하여 스레드가 즉시 정리되도록 할 수 있습니다
   with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
       # 로드 작업을 시작하고 각 퓨처의 해당 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
=======================

Added in version 3.14.

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 to 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=())

   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.

   참고:

     The executor may replace uncaught exceptions from *initializer*
     with "ExecutionFailed".

   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.

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" 서브 클래스입니다. "ProcessPoolExecutor" 는
"multiprocessing" 모듈을 사용합니다. *전역 인터프리터 록* 을 피할 수
있도록 하지만, 오직 피클 가능한 객체만 실행되고 반환될 수 있음을 의미
합니다.

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

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

Note that the restrictions on functions and arguments needing to
picklable as per "multiprocessing.Process" apply when using "submit()"
and "map()" on a "ProcessPoolExecutor". A function defined in a REPL
or a lambda should not be expected to work.

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

   최대 *max_workers* 프로세스의 풀을 사용하여 호출을 비동기적으로 실
   행하는 "Executor" 서브 클래스. *max_workers* 가 "None" 이거나 주어
   지지 않았다면, "os.process_cpu_count()"를 기본값으로 사용합니다.
   *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" 을
   발생시킵니다.

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

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

   버전 3.11에서 변경: *max_tasks_per_child* 인자가 추가되어 사용자가
   풀에 있는 작업자의 수명을 제어 할 수 있습니다.

   버전 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")".

   terminate_workers()

      Attempt to terminate all living worker processes immediately by
      calling "Process.terminate" on each of them. Internally, it will
      also call "Executor.shutdown()" to ensure that all other
      resources associated with the executor are freed.

      After calling this method the caller should no longer submit
      tasks to the executor.

      Added in version 3.14.

   kill_workers()

      Attempt to kill all living worker processes immediately by
      calling "Process.kill" on each of them. Internally, it will also
      call "Executor.shutdown()" to ensure that all other resources
      associated with the executor are freed.

      After calling this method the caller should no longer submit
      tasks to the executor.

      Added in version 3.14.


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* 초 내에 호출
      이 완료되지 않으면 "TimeoutError" 가 발생합니다. *timeout* 은
      int 또는 float가 될 수 있습니다. *timeout* 이 지정되지 않았거나
      "None" 인 경우, 대기 시간에는 제한이 없습니다.

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

      호출이 예외를 일으키는 경우, 이 메서드는 같은 예외를 발생시킵니
      다.

   exception(timeout=None)

      호출이 일으킨 예외를 돌려줍니다. 호출이 아직 완료되지 않는 경우,
      이 메서드는 *timeout* 초까지 대기합니다. *timeout* 초 내에 호출
      이 완료되지 않으면 "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)

   *fs* 로 주어진 여러 (서로 다른 "Executor" 인스턴스가 만든 것들도 가
   능합니다) "Future" 인스턴스들이 완료할 때까지 기다립니다. *fs* 로
   주어진 중복 퓨처는 제거되고 한 번만 반환됩니다. 집합들의 이름있는
   2-튜플을 돌려줍니다. "done" 이라는 이름의 첫 번째 집합은 대기가 끝
   나기 전에 완료된 (끝났거나 취소된) 퓨처를 담고 있습니다. "not_done"
   이라는 이름의 두 번째 집합은 완료되지 않은 (계류 중이거나 실행 중인
   ) 퓨처를 담고 있습니다.

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

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

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

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

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

더 보기:

  **PEP 3148** -- 퓨처 - 계산을 비동기적으로 실행
     파이썬 표준 라이브러리에 포함하기 위해, 이 기능을 설명한 제안.


예외 클래스
===========

exception concurrent.futures.CancelledError

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

exception concurrent.futures.TimeoutError

   "TimeoutError"의 폐지된 별칭, 퓨처 연산이 지정된 시간제한을 초과할
   때 발생합니다.

   버전 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

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

   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

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

   Added in version 3.3.
