# 코루틴과 태스크

이 절에서는 코루틴과 태스크로 작업하기 위한 고급 asyncio API에 관해 설명합니다.

## 코루틴

소스 코드: Lib/asyncio/coroutines.py

async/await 문법으로 선언된 코루틴은 asyncio 응용 프로그램을 작성하는 기본 방법입니다. 예를 들어, 다음 코드 조각은 “hello”를 인쇄하고, 1초 동안 기다린 다음, “world”를 인쇄합니다:

    >>> import asyncio

    >>> async def main():
    ...     print('hello')
    ...     await asyncio.sleep(1)
    ...     print('world')

    >>> asyncio.run(main())
    hello
    world

단지 코루틴을 호출하는 것으로 실행되도록 예약하는 것은 아닙니다:

    >>> main()
    <coroutine object main at 0x1053bb7c8>

코루틴을 실제로 실행하기 위해, asyncio가 다음과 같은 메커니즘을 제공합니다:

- 최상위 진입점 “main()” 함수를 실행하는 `asyncio.run()` 함수 (위의 예를 보세요.)

- 코루틴을 기다리기. 다음 코드 조각은 1초를 기다린 후 “hello”를 인쇄한 다음 또 2초를 기다린 후 “world”를 인쇄합니다:

      import asyncio
      import time

      async def say_after(delay, what):
          await asyncio.sleep(delay)
          print(what)

      async def main():
          print(f"started at {time.strftime('%X')}")

          await say_after(1, 'hello')
          await say_after(2, 'world')

          print(f"finished at {time.strftime('%X')}")

      asyncio.run(main())

  예상 출력:

      started at 17:13:52
      hello
      world
      finished at 17:13:55

- 코루틴을 asyncio `태스크`로 동시에 실행하는 `asyncio.create_task()` 함수.

  위의 예를 수정해서 두 개의 `say_after` 코루틴을 동시에 실행해 봅시다:

      async def main():
          task1 = asyncio.create_task(
              say_after(1, 'hello'))

          task2 = asyncio.create_task(
              say_after(2, 'world'))

          print(f"started at {time.strftime('%X')}")

          # 두 태스크가 모두 완료할 때까지 기다립니다 (2초 정도
          # 걸려야 합니다.)
          await task1
          await task2

          print(f"finished at {time.strftime('%X')}")

  예상 출력은 이제 코드 조각이 이전보다 1초 빠르게 실행되었음을 보여줍니다:

      started at 17:14:32
      hello
      world
      finished at 17:14:34

- The `asyncio.TaskGroup` class provides a more modern alternative to `create_task()`. Using this API, the last example becomes:

      async def main():
          async with asyncio.TaskGroup() as tg:
              task1 = tg.create_task(
                  say_after(1, 'hello'))

              task2 = tg.create_task(
                  say_after(2, 'world'))

              print(f"started at {time.strftime('%X')}")

          # 컨텍스트 매니저를 빠져나갈 때 어웨이트는 묵시적입니다.

          print(f"finished at {time.strftime('%X')}")

  The timing and output should be the same as for the previous version.

  Added in version 3.11: `asyncio.TaskGroup`.

## 어웨이터블

우리는 객체가 `await` 표현식에서 사용될 수 있을 때 어웨이터블 객체라고 말합니다. 많은 asyncio API는 어웨이터블을 받아들이도록 설계되었습니다.

어웨이터블 객체에는 세 가지 주요 유형이 있습니다: 코루틴, 태스크 및 퓨처.

코루틴

파이썬 코루틴은 어웨이터블이므로 다른 코루틴에서 기다릴 수 있습니다:

    import asyncio

    async def nested():
        return 42

    async def main():
        # 단지 "nested()" 를 호출하면 아무 일도 일어나지 않습니다.
        # 코루틴 객체가 생성되었지만 기다리지 않았습니다.
        # 따라서 *전혀 실행되지 않습니다*.
        nested()  # will raise a "RuntimeWarning".

        # 이제 다른 식으로 해봅시다, 기다립니다:
        print(await nested())  # "42" 를 인쇄합니다.

    asyncio.run(main())

중요

이 설명서에서 “코루틴” 이라는 용어는 두 가지 밀접한 관련 개념에 사용될 수 있습니다:

- 코루틴 함수: `async def` 함수;

- 코루틴 객체: 코루틴 함수를 호출하여 반환된 객체.

태스크

태스크는 코루틴을 동시에 예약하는 데 사용됩니다.

코루틴이 `asyncio.create_task()`와 같은 함수를 사용하여 태스크로 싸일 때 코루틴은 곧 실행되도록 자동으로 예약됩니다:

    import asyncio

    async def nested():
        return 42

    async def main():
        # nested()가 곧 "main()" 과 동시에 실행되도록
        # 예약합니다.
        task = asyncio.create_task(nested())

        # "task" 는 이제 "nested()" 를 취소하는 데 사용하거나,
        # 단순히 완료할 때까지 기다릴 수 있습니다:
        await task

    asyncio.run(main())

퓨처

`Future`는 비동기 연산의 최종 결과를 나타내는 특별한 저수준 어웨이터블 객체입니다.

Future 객체를 기다릴 때, 그것은 코루틴이 Future가 다른 곳에서 해결될 때까지 기다릴 것을 뜻합니다.

콜백 기반 코드를 async/await와 함께 사용하려면 asyncio의 Future 객체가 필요합니다.

일반적으로 응용 프로그램 수준 코드에서 Future 객체를 만들 필요는 없습니다.

때때로 라이브러리와 일부 asyncio API에 의해 노출되는 Future 객체를 기다릴 수 있습니다:

    async def main():
        await function_that_returns_a_future_object()

        # 이것도 유효합니다:
        await asyncio.gather(
            function_that_returns_a_future_object(),
            some_python_coroutine()
        )

Future 객체를 반환하는 저수준 함수의 좋은 예는 `loop.run_in_executor()`입니다.

## 태스크 만들기

소스 코드: Lib/asyncio/tasks.py

asyncio.create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)

  coro 코루틴을 `Task`로 감싸고 실행을 예약합니다. Task 객체를 반환합니다.

  The full function signature is largely the same as that of the `Task` constructor (or factory) - all of the keyword arguments to this function are passed through to that interface.

  An optional keyword-only context argument allows specifying a custom `contextvars.Context` for the coro to run in. The current context copy is created when no context is provided.

  An optional keyword-only eager_start argument allows specifying if the task should execute eagerly during the call to create_task, or be scheduled later. If eager_start is not passed the mode set by `loop.set_task_factory()` will be used.

  `get_running_loop()`에 의해 반환된 루프에서 태스크가 실행되고, 현재 스레드에 실행 중인 루프가 없으면 `RuntimeError`가 발생합니다.

  참고

  `asyncio.TaskGroup.create_task()` is a new alternative leveraging structural concurrency; it allows for waiting for a group of related tasks with strong safety guarantees.

  중요

  Save a reference to the result of this function, to avoid a task disappearing mid-execution. The event loop only keeps weak references to tasks. A task that isn’t referenced elsewhere may get garbage collected at any time, even before it’s done. For reliable “fire-and-forget” background tasks, gather them in a collection:

      background_tasks = set()

      for i in range(10):
          task = asyncio.create_task(some_coro(param=i))

          # 태스크를 집합에 추가합니다. 이는 강한 참조를 만듭니다.
          background_tasks.add(task)

          # 완료된 태스크에 대한 참조를 영원히 유지하지 않도록 하려면,
          # 각 태스크가 완료 후에 집합에서 자신의 참조를 제거하도록
          # 하십시오:
          task.add_done_callback(background_tasks.discard)

  Added in version 3.7.

  버전 3.8에서 변경: name 매개 변수가 추가되었습니다.

  버전 3.11에서 변경: context 매개 변수가 추가되었습니다.

  버전 3.14에서 변경: Added the eager_start parameter by passing on all kwargs.

## 태스크 취소

Tasks can easily and safely be cancelled. When a task is cancelled, `asyncio.CancelledError` will be raised in the task at the next opportunity.

It is recommended that coroutines use `try/finally` blocks to robustly perform clean-up logic. In case `asyncio.CancelledError` is explicitly caught, it should generally be propagated when clean-up is complete. `asyncio.CancelledError` directly subclasses `BaseException` so most code will not need to be aware of it.

The asyncio components that enable structured concurrency, like `asyncio.TaskGroup` and `asyncio.timeout()`, are implemented using cancellation internally and might misbehave if a coroutine swallows `asyncio.CancelledError`. Similarly, user code should not generally call `uncancel`. However, in cases when suppressing `asyncio.CancelledError` is truly desired, it is necessary to also call `uncancel()` to completely remove the cancellation state.

## 태스크 그룹

Task groups combine a task creation API with a convenient and reliable way to wait for all tasks in the group to finish.

class asyncio.TaskGroup

  An asynchronous context manager holding a group of tasks. Tasks can be added to the group using `create_task()`. All tasks are awaited when the context manager exits.

  Added in version 3.11.

  create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)

    Create a task in this task group. The signature matches that of `asyncio.create_task()`. If the task group is inactive (e.g. not yet entered, already finished, or in the process of shutting down), we will close the given `coro`.

    버전 3.13에서 변경: Close the given coroutine if the task group is not active.

    버전 3.14에서 변경: Passes on all kwargs to `loop.create_task()`

예:

    async def main():
        async with asyncio.TaskGroup() as tg:
            task1 = tg.create_task(some_coro(...))
            task2 = tg.create_task(another_coro(...))
        print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")

The `async with` statement will wait for all tasks in the group to finish. While waiting, new tasks may still be added to the group (for example, by passing `tg` into one of the coroutines and calling `tg.create_task()` in that coroutine). Once the last task has finished and the `async with` block is exited, no new tasks may be added to the group.

The first time any of the tasks belonging to the group fails with an exception other than `asyncio.CancelledError`, the remaining tasks in the group are cancelled. No further tasks can then be added to the group. At this point, if the body of the `async with` statement is still active (i.e., `__aexit__()` hasn’t been called yet), the task directly containing the `async with` statement is also cancelled. The resulting `asyncio.CancelledError` will interrupt an `await`, but it will not bubble out of the containing `async with` statement.

Once all tasks have finished, if any tasks have failed with an exception other than `asyncio.CancelledError`, those exceptions are combined in an `ExceptionGroup` or `BaseExceptionGroup` (as appropriate; see their documentation) which is then raised.

Two base exceptions are treated specially: If any task fails with `KeyboardInterrupt` or `SystemExit`, the task group still cancels the remaining tasks and waits for them, but then the initial `KeyboardInterrupt` or `SystemExit` is re-raised instead of `ExceptionGroup` or `BaseExceptionGroup`.

If the body of the `async with` statement exits with an exception (so `__aexit__()` is called with an exception set), this is treated the same as if one of the tasks failed: the remaining tasks are cancelled and then waited for, and non-cancellation exceptions are grouped into an exception group and raised. The exception passed into `__aexit__()`, unless it is `asyncio.CancelledError`, is also included in the exception group. The same special case is made for `KeyboardInterrupt` and `SystemExit` as in the previous paragraph.

Task groups are careful not to mix up the internal cancellation used to “wake up” their `__aexit__()` with cancellation requests for the task in which they are running made by other parties. In particular, when one task group is syntactically nested in another, and both experience an exception in one of their child tasks simultaneously, the inner task group will process its exceptions, and then the outer task group will receive another cancellation and process its own exceptions.

In the case where a task group is cancelled externally and also must raise an `ExceptionGroup`, it will call the parent task’s `cancel()` method. This ensures that a `asyncio.CancelledError` will be raised at the next `await`, so the cancellation is not lost.

Task groups preserve the cancellation count reported by `asyncio.Task.cancelling()`.

버전 3.13에서 변경: Improved handling of simultaneous internal and external cancellations and correct preservation of cancellation counts.

### Terminating a Task Group

While terminating a task group is not natively supported by the standard library, termination can be achieved by adding an exception-raising task to the task group and ignoring the raised exception:

    import asyncio
    from asyncio import TaskGroup

    class TerminateTaskGroup(Exception):
        """태스크 그룹 종료하려고 발생시키는 예외."""

    async def force_terminate_task_group():
        """태스크 그룹의 강제 종료에 사용됩니다."""
        raise TerminateTaskGroup()

    async def job(task_id, sleep_time):
        print(f'Task {task_id}: start')
        await asyncio.sleep(sleep_time)
        print(f'Task {task_id}: done')

    async def main():
        try:
            async with TaskGroup() as group:
                # 태스크를 몇 개 만듭니다
                group.create_task(job(1, 0.5))
                group.create_task(job(2, 1.5))
                # 1초 동안 잠잡니다
                await asyncio.sleep(1)
                # 그룹을 강제 종료하기 위해 예외를 일으키는 태스크를 추가합니다
                group.create_task(force_terminate_task_group())
        except* TerminateTaskGroup:
            pass

    asyncio.run(main())

예상 출력:

    Task 1: start
    Task 2: start
    Task 1: done

## 잠자기

async asyncio.sleep(delay, result=None)

  delay 초 동안 블록합니다.

  result가 제공되면, 코루틴이 완료될 때 호출자에게 반환됩니다.

  `sleep()`은 항상 현재 태스크를 일시 중단해서 다른 태스크를 실행할 수 있도록 합니다.

  Setting the delay to 0 provides an optimized path to allow other tasks to run. This can be used by long-running functions to avoid blocking the event loop for the full duration of the function call.

  5초 동안 현재 날짜를 매초 표시하는 코루틴의 예:

      import asyncio
      import datetime

      async def display_date():
          loop = asyncio.get_running_loop()
          end_time = loop.time() + 5.0
          while True:
              print(datetime.datetime.now())
              if (loop.time() + 1.0) >= end_time:
                  break
              await asyncio.sleep(1)

      asyncio.run(display_date())

  버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

  버전 3.13에서 변경: Raises `ValueError` if delay is `nan`.

## 동시에 태스크 실행하기

awaitable asyncio.gather(*aws, return_exceptions=False)

  aws 시퀀스에 있는 어웨이터블 객체를 동시에 실행합니다.

  aws에 있는 어웨이터블이 코루틴이면 자동으로 태스크로 예약됩니다.

  모든 어웨이터블이 성공적으로 완료되면, 결과는 반환된 값들이 합쳐진 리스트입니다. 결괏값의 순서는 aws에 있는 어웨이터블의 순서와 일치합니다.

  return_exceptions가 `False`(기본값)면, 첫 번째 발생한 예외가 `gather()`를 기다리는 태스크로 즉시 전파됩니다. aws 시퀀스의 다른 어웨이터블은 취소되지 않고 계속 실행됩니다.

  return_exceptions가 `True`면, 예외는 성공적인 결과처럼 처리되고, 결과 리스트에 집계됩니다.

  `gather()`가 취소되면, 모든 제출된 (아직 완료되지 않은) 어웨이터블도 취소됩니다.

  aws 시퀀스의 Task나 Future가 취소되면, 그것이 `CancelledError`를 일으킨 것처럼 처리됩니다 – 이때 `gather()` 호출은 취소되지 않습니다. 이것은 제출된 태스크/퓨처 하나를 취소하는 것이 다른 태스크/퓨처를 취소하게 되는 것을 막기 위한 것입니다.

  참고

  A new alternative to create and run tasks concurrently and wait for their completion is `asyncio.TaskGroup`. TaskGroup provides stronger safety guarantees than gather for scheduling a nesting of subtasks: if a task (or a subtask, a task scheduled by a task) raises an exception, TaskGroup will, while gather will not, cancel the remaining scheduled tasks).

  예:

      import asyncio

      async def factorial(name, number):
          f = 1
          for i in range(2, number + 1):
              print(f"Task {name}: Compute factorial({number}), currently i={i}...")
              await asyncio.sleep(1)
              f *= i
          print(f"Task {name}: factorial({number}) = {f}")
          return f

      async def main():
          # 3개의 호출을 *동시에* 예약합니다:
          L = await asyncio.gather(
              factorial("A", 2),
              factorial("B", 3),
              factorial("C", 4),
          )
          print(L)

      asyncio.run(main())

      # 예상 출력:
      #
      #     Task A: Compute factorial(2), currently i=2...
      #     Task B: Compute factorial(3), currently i=2...
      #     Task C: Compute factorial(4), currently i=2...
      #     Task A: factorial(2) = 2
      #     Task B: Compute factorial(3), currently i=3...
      #     Task C: Compute factorial(4), currently i=3...
      #     Task B: factorial(3) = 6
      #     Task C: Compute factorial(4), currently i=4...
      #     Task C: factorial(4) = 24
      #     [2, 6, 24]

  참고

  return_exceptions가 거짓이면, 완료로 표시된 후 gather()를 취소하는 것은 제출된 어웨이터블을 취소하지 않습니다. 예를 들어, 예외를 호출자에게 전파한 후 gather가 완료된 것으로 표시될 수 있습니다, 따라서 gather에서 (어웨이터블 중 하나에 의해 발생한) 예외를 포착한 후 `gather.cancel()`을 호출하는 것은 다른 어웨이터블을 취소하지 않습니다.

  버전 3.7에서 변경: gather 자체가 취소되면, return_exceptions와 관계없이 취소가 전파됩니다.

  버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

  버전 3.10부터 폐지됨: Deprecation warning is emitted if no positional arguments are provided or not all positional arguments are Future-like objects and there is no running event loop.

## Eager Task Factory

asyncio.eager_task_factory(loop, coro, *, name=None, context=None)

  A task factory for eager task execution.

  When using this factory (via `loop.set_task_factory(asyncio.eager_task_factory)`), coroutines begin execution synchronously during `Task` construction. Tasks are only scheduled on the event loop if they block. This can be a performance improvement as the overhead of loop scheduling is avoided for coroutines that complete synchronously.

  A common example where this is beneficial is coroutines which employ caching or memoization to avoid actual I/O when possible.

  참고

  Immediate execution of the coroutine is a semantic change. If the coroutine returns or raises, the task is never scheduled to the event loop. If the coroutine execution blocks, the task is scheduled to the event loop. This change may introduce behavior changes to existing applications. For example, the application’s task execution order is likely to change.

  Added in version 3.12.

asyncio.create_eager_task_factory(custom_task_constructor)

  Create an eager task factory, similar to `eager_task_factory()`, using the provided custom_task_constructor when creating a new task instead of the default `Task`.

  custom_task_constructor must be a callable with the signature matching the signature of `Task.__init__`. The callable must return a `asyncio.Task`-compatible object.

  This function returns a callable intended to be used as a task factory of an event loop via `loop.set_task_factory(factory)`).

  Added in version 3.12.

## 취소로부터 보호하기

awaitable asyncio.shield(aw)

  어웨이터블 객체를 `취소`로부터 보호합니다.

  aw가 코루틴이면 자동으로 태스크로 예약됩니다.

  다음 문장:

      task = asyncio.create_task(something())
      res = await shield(task)

  은 다음과 동등합니다:

      res = await something()

  단, 그것을 포함하는 코루틴이 취소되면, `something()`에서 실행 중인 태스크는 취소되지 않는다는 것만 예외입니다. `something()`의 관점에서는, 취소가 일어나지 않았습니다. 호출자는 여전히 취소되었고, “await” 표현식은 여전히 `CancelledError`를 발생시킵니다.

  `something()`가 다른 수단(즉, 그 안에서 스스로)에 의해 취소되면, `shield()`도 취소됩니다.

  취소를 완전히 무시하려면(권장되지 않습니다), 다음과 같이 `shield()` 함수를 try/except 절과 결합해야 합니다:

      task = asyncio.create_task(something())
      try:
          res = await shield(task)
      except CancelledError:
          res = None

  중요

  Save a reference to tasks passed to this function, to avoid a task disappearing mid-execution. The event loop only keeps weak references to tasks. A task that isn’t referenced elsewhere may get garbage collected at any time, even before it’s done.

  버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

  버전 3.10부터 폐지됨: Deprecation warning is emitted if aw is not Future-like object and there is no running event loop.

## 시간제한

asyncio.timeout(delay)

  Return an asynchronous context manager that can be used to limit the amount of time spent waiting on something.

  delay는 `None` 또는 대기할 float/int 초 수입니다. delay가 `None`이면, 시간 제한이 적용되지 않습니다; 컨텍스트 관리자가 만들어질 때 지연 시간이 아려지지 않은 경우 유용할 수 있습니다.

  In either case, the context manager can be rescheduled after creation using `Timeout.reschedule()`.

  예:

      async def main():
          async with asyncio.timeout(10):
              await long_running_task()

  If `long_running_task` takes more than 10 seconds to complete, the context manager will cancel the current task and handle the resulting `asyncio.CancelledError` internally, transforming it into a `TimeoutError` which can be caught and handled.

  참고

  The `asyncio.timeout()` context manager is what transforms the `asyncio.CancelledError` into a `TimeoutError`, which means the `TimeoutError` can only be caught outside of the context manager.

  Example of catching `TimeoutError`:

      async def main():
          try:
              async with asyncio.timeout(10):
                  await long_running_task()
          except TimeoutError:
              print("The long operation timed out, but we've handled it.")

          print("This statement will run regardless.")

  The context manager produced by `asyncio.timeout()` can be rescheduled to a different deadline and inspected.

  class asyncio.Timeout(when)

    An asynchronous context manager for cancelling overdue coroutines.

    `when` should be an absolute time at which the context should time out, as measured by the event loop’s clock:

    - If `when` is `None`, the timeout will never trigger.

    - If `when < loop.time()`, the timeout will trigger on the next iteration of the event loop.

    > when() → float | None

    >   Return the current deadline, or `None` if the current deadline is not set.

    > reschedule(when: float | None)

    >   Reschedule the timeout.

    > expired() → bool

    >   Return whether the context manager has exceeded its deadline (expired).

  예:

      async def main():
          try:
              # 시작 시 시간 제한을 알 수 없어서 ``None`` 을 전달합니다.
              async with asyncio.timeout(None) as cm:
                  # 이제 시간 제한을 알게되어, 재조정합니다.
                  new_deadline = get_running_loop().time() + 10
                  cm.reschedule(new_deadline)

                  await long_running_task()
          except TimeoutError:
              pass

          if cm.expired():
              print("Looks like we haven't finished on time.")

  Timeout context managers can be safely nested.

  Added in version 3.11.

asyncio.timeout_at(when)

  Similar to `asyncio.timeout()`, except when is the absolute time to stop waiting, or `None`.

  예:

      async def main():
          loop = get_running_loop()
          deadline = loop.time() + 20
          try:
              async with asyncio.timeout_at(deadline):
                  await long_running_task()
          except TimeoutError:
              print("The long operation timed out, but we've handled it.")

          print("This statement will run regardless.")

  Added in version 3.11.

async asyncio.wait_for(aw, timeout)

  aw 어웨이터블이 제한된 시간 내에 완료될 때까지 기다립니다.

  aw가 코루틴이면 자동으로 태스크로 예약됩니다.

  timeout은 `None` 또는 대기할 float 나 int 초 수입니다. timeout이 `None`이면 퓨처가 완료될 때까지 블록합니다.

  시간 초과가 발생하면, 태스크를 취소하고 `TimeoutError`를 발생시킵니다.

  태스크 `취소`를 피하려면, `shield()`로 감싸십시오.

  이 함수는 퓨처가 실제로 취소될 때까지 대기하므로, 총 대기 시간이 timeout을 초과할 수 있습니다. 취소하는 동안 예외가 발생하면, 전파됩니다.

  대기가 취소되면, 퓨처 aw도 취소됩니다.

  예:

      async def eternity():
          # 1시간 동안 잠잡니다
          await asyncio.sleep(3600)
          print('yay!')

      async def main():
          # 최대 1초간 대기합니다
          try:
              await asyncio.wait_for(eternity(), timeout=1.0)
          except TimeoutError:
              print('timeout!')

      asyncio.run(main())

      # 예상 출력:
      #
      #     timeout!

  버전 3.7에서 변경: 시간 초과로 인해 aw가 취소되면, `wait_for`는 aw가 취소될 때까지 대기합니다. 이전에는 `TimeoutError`가 즉시 발생했습니다.

  버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

  버전 3.11에서 변경: Raises `TimeoutError` instead of `asyncio.TimeoutError`.

## 대기 프리미티브

async asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)

  aws 이터러블에 있는 `Future`와 `Task` 인스턴스를 동시에 실행하고, return_when에 의해 지정된 조건을 만족할 때까지 블록합니다.

  aws 이터러블은 비어있을 수 없습니다.

  두 집합의 태스크/퓨처를 반환합니다: `(done, pending)`.

  사용법:

      done, pending = await asyncio.wait(aws)

  timeout(float나 int)을 지정하면, 반환하기 전에 대기할 최대 시간(초)을 제어할 수 있습니다.

  이 함수는 `TimeoutError`를 발생시키지 않음에 유의하십시오. 시간 초과가 발생할 때 완료되지 않은 퓨처나 태스크는 단순히 두 번째 집합으로 반환됩니다.

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

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

  `wait_for()`와 달리, `wait()`는 시간 초과가 발생할 때 퓨처를 취소하지 않습니다.

  버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

  버전 3.11에서 변경: 코루틴 객체를 `wait()`로 직접 전달하는 것은 금지됩니다.

  버전 3.12에서 변경: Added support for generators yielding tasks.

asyncio.as_completed(aws, *, timeout=None)

  aws 이터러블에 있는 어웨이터블 객체를 동시에 실행합니다. 코루틴의 이터레이터를 반환합니다. 반환된 객체는 어웨이터블이 완료될 때마다 그 결과를 얻기 위해 이터레이트 할 수 있습니다.

  The object returned by `as_completed()` can be iterated as an asynchronous iterator or a plain iterator. When asynchronous iteration is used, the originally-supplied awaitables are yielded if they are tasks or futures. This makes it easy to correlate previously-scheduled tasks with their results. Example:

      ipv4_connect = create_task(open_connection("127.0.0.1", 80))
      ipv6_connect = create_task(open_connection("::1", 80))
      tasks = [ipv4_connect, ipv6_connect]

      async for earliest_connect in as_completed(tasks):
          # earliest_connect 가 완료했습니다. 결과는 어웨이트하거나
          # earliest_connect.result() 를 호출해서 얻을 수 있습니다.
          reader, writer = await earliest_connect

          if earliest_connect is ipv6_connect:
              print("IPv6 connection established.")
          else:
              print("IPv4 connection established.")

  During asynchronous iteration, implicitly-created tasks will be yielded for supplied awaitables that aren’t tasks or futures.

  When used as a plain iterator, each iteration yields a new coroutine that returns the result or raises the exception of the next completed awaitable. This pattern is compatible with Python versions older than 3.13:

      ipv4_connect = create_task(open_connection("127.0.0.1", 80))
      ipv6_connect = create_task(open_connection("::1", 80))
      tasks = [ipv4_connect, ipv6_connect]

      for next_connect in as_completed(tasks):
          # next_connect 는 원래 태스크 객체 중 하나가 아닙니다.
          # 결과 값을 얻거나 다음에 완료되는 어웨이터블의 예외를 발생시키려면
          # 반드시 어웨이트해야 합니다.
          reader, writer = await next_connect

  A `TimeoutError` is raised if the timeout occurs before all awaitables are done. This is raised by the `async for` loop during asynchronous iteration or by the coroutines yielded during plain iteration.

  버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

  버전 3.10부터 폐지됨: Deprecation warning is emitted if not all awaitable objects in the aws iterable are Future-like objects and there is no running event loop.

  버전 3.12에서 변경: Added support for generators yielding tasks.

  버전 3.13에서 변경: The result can now be used as either an asynchronous iterator or as a plain iterator (previously it was only a plain iterator).

## 스레드에서 실행하기

async asyncio.to_thread(func, /, *args, **kwargs)

  별도의 스레드에서 func 함수를 비동기적으로 실행합니다.

  이 함수에 제공된 모든 *args 와 **kwargs 는 func로 직접 전달됩니다. 또한, 현재 `contextvars.Context`가 전파되어, 이벤트 루프 스레드의 컨텍스트 변수가 별도의 스레드에서 액세스 될 수 있습니다.

  func의 최종 결과를 얻기 위해 어웨이트 할 수 있는 코루틴을 반환합니다.

  이 코루틴 함수는 메인 스레드에서 실행된다면 이벤트 루프를 블록할 IO 병목 함수/메서드를 실행하는 데 주로 사용됩니다. 예를 들면:

      def blocking_io():
          print(f"start blocking_io at {time.strftime('%X')}")
          # time.sleep()은 파일 연산과 같은 임의의 블로킹 IO 병목 연산으로 대체될
          # 수 있음에 유의하십시오.
          time.sleep(1)
          print(f"blocking_io complete at {time.strftime('%X')}")

      async def main():
          print(f"started main at {time.strftime('%X')}")

          await asyncio.gather(
              asyncio.to_thread(blocking_io),
              asyncio.sleep(1))

          print(f"finished main at {time.strftime('%X')}")

      asyncio.run(main())

      # 예상 출력:
      #
      # started main at 19:50:53
      # start blocking_io at 19:50:53
      # blocking_io complete at 19:50:54
      # finished main at 19:50:54

  코루틴에서 `blocking_io()`를 직접 호출하면 그동안 이벤트 루프가 블록 되어 추가 1초의 실행 시간이 발생합니다. 대신, `asyncio.to_thread()`를 사용하면, 이벤트 루프를 블록하지 않고 별도의 스레드에서 실행할 수 있습니다.

  참고

  GIL로 인해, `asyncio.to_thread()`는 일반적으로 IO 병목 함수를 비 블로킹으로 만드는 데만 사용할 수 있습니다. 그러나, GIL을 반납하는 확장 모듈이나 GIL이 없는 대체 파이썬 구현의 경우, `asyncio.to_thread()`를 CPU 병목 함수에도 사용할 수 있습니다.

  Added in version 3.9.

## 다른 스레드에서 예약하기

asyncio.run_coroutine_threadsafe(coro, loop)

  주어진 이벤트 루프에 코루틴을 제출합니다. 스레드 안전합니다.

  다른 OS 스레드에서 결과를 기다리는 `concurrent.futures.Future`를 반환합니다.

  이 함수는 이벤트 루프가 실행 중인 스레드가 아닌, 다른 OS 스레드에서 호출하기 위한 것입니다. 예:

      def in_thread(loop: asyncio.AbstractEventLoop) -> None:
          # Run some blocking IO
          pathlib.Path("example.txt").write_text("hello world", encoding="utf8")

          # Create a coroutine
          coro = asyncio.sleep(1, result=3)

          # Submit the coroutine to a given loop
          future = asyncio.run_coroutine_threadsafe(coro, loop)

          # Wait for the result with an optional timeout argument
          assert future.result(timeout=2) == 3

      async def amain() -> None:
          # Get the running loop
          loop = asyncio.get_running_loop()

          # Run something in a thread
          await asyncio.to_thread(in_thread, loop)

  It’s also possible to run the other way around. Example:

      @contextlib.contextmanager
      def loop_in_thread() -> Generator[asyncio.AbstractEventLoop]:
          loop_fut = concurrent.futures.Future[asyncio.AbstractEventLoop]()
          stop_event = asyncio.Event()

          async def main() -> None:
              loop_fut.set_result(asyncio.get_running_loop())
              await stop_event.wait()

          with concurrent.futures.ThreadPoolExecutor(1) as tpe:
              complete_fut = tpe.submit(asyncio.run, main())
              for fut in concurrent.futures.as_completed((loop_fut, complete_fut)):
                  if fut is loop_fut:
                      loop = loop_fut.result()
                      try:
                          yield loop
                      finally:
                          loop.call_soon_threadsafe(stop_event.set)
                  else:
                      fut.result()

      # Create a loop in another thread
      with loop_in_thread() as loop:
          # Create a coroutine
          coro = asyncio.sleep(1, result=3)

          # Submit the coroutine to a given loop
          future = asyncio.run_coroutine_threadsafe(coro, loop)

          # Wait for the result with an optional timeout argument
          assert future.result(timeout=2) == 3

  코루틴에서 예외가 발생하면, 반환된 Future에 통지됩니다. 또한, 이벤트 루프에서 태스크를 취소하는 데 사용할 수 있습니다:

      try:
          result = future.result(timeout)
      except TimeoutError:
          print('The coroutine took too long, cancelling the task...')
          future.cancel()
      except Exception as exc:
          print(f'The coroutine raised an exception: {exc!r}')
      else:
          print(f'The coroutine returned: {result!r}')

  설명서의 동시성과 다중 스레드 절을 참조하십시오.

  다른 asyncio 함수와 달리, 이 함수는 loop 인자가 명시적으로 전달되어야 합니다.

  Added in version 3.5.1.

## 인트로스펙션

asyncio.current_task(loop=None)

  현재 실행 중인 `Task` 인스턴스를 반환하거나 태스크가 실행되고 있지 않으면 `None`을 반환합니다.

  loop가 `None`이면, 현재 루프를 가져오는 데 `get_running_loop()`가 사용됩니다.

  Added in version 3.7.

asyncio.all_tasks(loop=None)

  루프에 의해 실행되는 아직 완료되지 않은 `Task` 객체 집합을 반환합니다.

  loop가 `None`이면, 현재 루프를 가져오는 데 `get_running_loop()`가 사용됩니다.

  Added in version 3.7.

asyncio.iscoroutine(obj)

  obj가 코루틴 객체면 `True`를 반환합니다.

  Added in version 3.4.

## Task 객체

class asyncio.Task(coro, *, loop=None, name=None, context=None, eager_start=False)

  파이썬 코루틴을 실행하는 `퓨처류` 객체입니다. 스레드 안전하지 않습니다.

  태스크는 이벤트 루프에서 코루틴을 실행하는 데 사용됩니다. 만약 코루틴이 Future를 기다리고 있다면, 태스크는 코루틴의 실행을 일시 중지하고 Future의 완료를 기다립니다. 퓨처가 완료되면, 감싸진 코루틴의 실행이 다시 시작됩니다.

  이벤트 루프는 협업 스케줄링을 사용합니다: 이벤트 루프는 한 번에 하나의 Task를 실행합니다. Task가 Future의 완료를 기다리는 동안, 이벤트 루프는 다른 태스크, 콜백을 실행하거나 IO 연산을 수행합니다.

  테스크를 만들려면 고수준 `asyncio.create_task()` 함수를 사용하거나, 저수준 `loop.create_task()` 나 `ensure_future()` 함수를 사용하십시오. 태스크의 인스턴스를 직접 만드는 것은 권장되지 않습니다.

  실행 중인 Task를 취소하려면 `cancel()` 메서드를 사용하십시오. 이를 호출하면 태스크가 감싼 코루틴으로 `CancelledError` 예외를 던집니다. 코루틴이 취소 중에 Future 객체를 기다리고 있으면, Future 객체가 취소됩니다.

  `cancelled()`는 태스크가 취소되었는지 확인하는 데 사용할 수 있습니다. 이 메서드는 감싼 코루틴이 `CancelledError` 예외를 억제하지 않고 실제로 취소되었으면 `True`를 반환합니다.

  `asyncio.Task`는 `Future.set_result()`와 `Future.set_exception()`을 제외한 모든 API를 `Future`에서 상속받습니다.

  An optional keyword-only context argument allows specifying a custom `contextvars.Context` for the coro to run in. If no context is provided, the Task copies the current context and later runs its coroutine in the copied context.

  An optional keyword-only eager_start argument allows eagerly starting the execution of the `asyncio.Task` at task creation time. If set to `True` and the event loop is running, the task will start executing the coroutine immediately, until the first time the coroutine blocks. If the coroutine returns or raises without blocking, the task will be finished eagerly and will skip scheduling to the event loop.

  버전 3.7에서 변경: `contextvars` 모듈에 대한 지원이 추가되었습니다.

  버전 3.8에서 변경: name 매개 변수가 추가되었습니다.

  버전 3.10부터 폐지됨: Deprecation warning is emitted if loop is not specified and there is no running event loop.

  버전 3.11에서 변경: context 매개 변수가 추가되었습니다.

  버전 3.12에서 변경: eager_start 매개 변수가 추가되었습니다.

  done()

    Task가 완료(done)되었으면 `True`를 반환합니다.

    감싼 코루틴이 값을 반환하거나 예외를 일으키거나, Task가 취소되면 Task는 완료(done)됩니다.

  result()

    Task의 결과를 반환합니다.

    Task가 완료(done)되었으면 감싼 코루틴의 결과가 반환됩니다 (또는 코루틴이 예외를 발생시켰으면 해당 예외가 다시 발생합니다).

    태스크가 취소(cancelled)되었으면, 이 메서드는 `CancelledError` 예외를 발생시킵니다.

    태스크 결과를 아직 사용할 수 없으면, 이 메서드는 `InvalidStateError` 예외를 발생시킵니다.

  exception()

    Task의 예외를 반환합니다.

    감싼 코루틴이 예외를 발생시키면, 그 예외가 반환됩니다. 감싼 코루틴이 정상적으로 반환되면, 이 메서드는 `None`을 반환합니다.

    태스크가 취소(cancelled)되었으면, 이 메서드는 `CancelledError` 예외를 발생시킵니다.

    태스크가 아직 완료(done)되지 않았으면, 이 메서드는 `InvalidStateError` 예외를 발생시킵니다.

  add_done_callback(callback, *, context=None)

    태스크가 완료(done)될 때 실행할 콜백을 추가합니다.

    이 메서드는 저수준 콜백 기반 코드에서만 사용해야 합니다.

    자세한 내용은 `Future.add_done_callback()` 설명서를 참조하십시오.

  remove_done_callback(callback)

    콜백 목록에서 callback을 제거합니다.

    이 메서드는 저수준 콜백 기반 코드에서만 사용해야 합니다.

    자세한 내용은 `Future.remove_done_callback()` 설명서를 참조하십시오.

  get_stack(*, limit=None)

    이 Task의 스택 프레임 리스트를 돌려줍니다.

    감싼 코루틴이 완료되지 않았으면, 일시 정지된 곳의 스택을 반환합니다. 코루틴이 성공적으로 완료되었거나 취소되었으면 빈 리스트가 반환됩니다. 코루틴이 예외로 종료되었으면, 이것은 트레이스백 프레임의 리스트를 반환합니다.

    프레임은 항상 가장 오래된 것부터 순서대로 정렬됩니다.

    일시 정지된 코루틴에서는 하나의 스택 프레임만 반환됩니다.

    선택적 limit 인자는 반환할 최대 프레임 수를 설정합니다; 기본적으로 사용 가능한 모든 프레임이 반환됩니다. 반환되는 리스트의 순서는 스택과 트레이스백 중 어느 것이 반환되는지에 따라 다릅니다: 스택은 최신 프레임이 반환되지만, 트레이스백은 가장 오래된 프레임이 반환됩니다. (이는 traceback 모듈의 동작과 일치합니다.)

  print_stack(*, limit=None, file=None)

    이 Task의 스택이나 트레이스백을 인쇄합니다.

    이것은 `get_stack()`으로 얻은 프레임에 대해 traceback 모듈과 유사한 출력을 생성합니다.

    limit 인자는 `get_stack()`에 직접 전달됩니다.

    file 인자는 출력이 기록되는 I/O 스트림입니다; 기본적으로 출력은 `sys.stdout`에 기록됩니다.

  get_coro()

    `Task`로 싸인 코루틴 객체를 반환합니다.

    참고

    This will return `None` for Tasks which have already completed eagerly. See the Eager Task Factory.

    Added in version 3.8.

    버전 3.12에서 변경: Newly added eager task execution means result may be `None`.

  get_context()

    Return the `contextvars.Context` object associated with the task.

    Added in version 3.12.

  get_name()

    Task의 이름을 반환합니다.

    Task에 명시적으로 이름이 지정되지 않으면, 기본 asyncio Task 구현은 인스턴스화 중에 기본 이름을 생성합니다.

    Added in version 3.8.

  set_name(value)

    Task의 이름을 설정합니다.

    value 인자는 모든 객체가 될 수 있으며, 문자열로 변환됩니다.

    기본 Task 구현에서, 이름은 태스크 객체의 `repr()` 출력에 표시됩니다.

    Added in version 3.8.

  cancel(msg=None)

    Task 취소를 요청합니다.

    If the Task is already done or cancelled, return `False`, otherwise, return `True`.

    이 메서드는 이벤트 루프의 다음 사이클에서 감싼 코루틴으로 `CancelledError` 예외를 던져넣도록 합니다.

    그러면 코루틴은 `try` … … `except CancelledError` … `finally` 블록으로 정리하거나 예외를 억제하여 요청을 거부할 수 있습니다. 따라서, `Future.cancel()`와 달리 `Task.cancel()`은 Task가 취소됨을 보장하지는 않습니다. 하지만 취소를 완전히 억제하는 것은 일반적이지 않고, 그렇게 하지 말도록 적극적으로 권합니다. 그럼에도 불구하고 코루틴이 취소를 억제하기로 결정한 경우, 예외를 잡는 것 외에 `Task.uncancel()` 호출도 해야 합니다.

    버전 3.9에서 변경: msg 매개 변수가 추가되었습니다.

    버전 3.11에서 변경: The `msg` parameter is propagated from cancelled task to its awaiter.

    다음 예는 코루틴이 취소 요청을 가로채는 방법을 보여줍니다:

        async def cancel_me():
            print('cancel_me(): before sleep')

            try:
                # 1시간 동안 기다립니다
                await asyncio.sleep(3600)
            except asyncio.CancelledError:
                print('cancel_me(): cancel sleep')
                raise
            finally:
                print('cancel_me(): after sleep')

        async def main():
            # "cancel_me" Task를 만듭니다
            task = asyncio.create_task(cancel_me())

            # 1초 동안 기다립니다
            await asyncio.sleep(1)

            task.cancel()
            try:
                await task
            except asyncio.CancelledError:
                print("main(): cancel_me is cancelled now")

        asyncio.run(main())

        # 예상 출력:
        #
        #     cancel_me(): before sleep
        #     cancel_me(): cancel sleep
        #     cancel_me(): after sleep
        #     main(): cancel_me is cancelled now

  cancelled()

    Task가 취소(cancelled)되었으면 `True`를 반환합니다.

    Task는 `cancel()`로 취소가 요청되고 감싼 코루틴이 자신에게 전달된 `CancelledError` 예외를 확산할 때 최소(cancelled)됩니다.

  uncancel()

    Decrement the count of cancellation requests to this Task.

    Returns the remaining number of cancellation requests.

    Note that once execution of a cancelled task completed, further calls to `uncancel()` are ineffective.

    Added in version 3.11.

    This method is used by asyncio’s internals and isn’t expected to be used by end-user code. In particular, if a Task gets successfully uncancelled, this allows for elements of structured concurrency like 태스크 그룹 and `asyncio.timeout()` to continue running, isolating cancellation to the respective structured block. For example:

        async def make_request_with_timeout():
            try:
                async with asyncio.timeout(1):
                    # 시간 제한의 영향을 받는 구조화된 블록:
                    await make_request()
                    await make_another_request()
            except TimeoutError:
                log("There was a timeout")
            # 시간 제한의 영향을 받지 않는 바깥 코드:
            await unrelated_code()

    While the block with `make_request()` and `make_another_request()` might get cancelled due to the timeout, `unrelated_code()` should continue running even in case of the timeout. This is implemented with `uncancel()`. `TaskGroup` context managers use `uncancel()` in a similar fashion.

    If end-user code is, for some reason, suppressing cancellation by catching `CancelledError`, it needs to call this method to remove the cancellation state.

    When this method decrements the cancellation count to zero, the method checks if a previous `cancel()` call had arranged for `CancelledError` to be thrown into the task. If it hasn’t been thrown yet, that arrangement will be rescinded (by resetting the internal `_must_cancel` flag).

  버전 3.13에서 변경: Changed to rescind pending cancellation requests upon reaching zero.

  cancelling()

    Return the number of pending cancellation requests to this Task, i.e., the number of calls to `cancel()` less the number of `uncancel()` calls.

    Note that if this number is greater than zero but the Task is still executing, `cancelled()` will still return `False`. This is because this number can be lowered by calling `uncancel()`, which can lead to the task not being cancelled after all if the cancellation requests go down to zero.

    This method is used by asyncio’s internals and isn’t expected to be used by end-user code. See `uncancel()` for more details.

    Added in version 3.11.