协程和任务

本节概述了用于处理协程和任务的高级 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 提供了以下机制

  • asyncio.run() 函数用于运行顶层入口点“main()”函数(参见上面的示例)。

  • 等待协程。以下代码片段将在等待 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.create_task() 函数用于将协程并发地运行为 asyncio Tasks

    让我们修改上面的示例,并并发地运行两个 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')}")
    
        # Wait until both tasks are completed (should take
        # around 2 seconds.)
        await task1
        await task2
    
        print(f"finished at {time.strftime('%X')}")
    

    请注意,预期输出现在显示该代码片段比以前快了 1 秒

    started at 17:14:32
    hello
    world
    finished at 17:14:34
    
  • asyncio.TaskGroup 类为 create_task() 提供了更现代的替代方案。使用此 API,最后一个示例变为

    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')}")
    
        # The await is implicit when the context manager exits.
    
        print(f"finished at {time.strftime('%X')}")
    

    时间和输出应该与先前版本相同。

    在版本 3.11 中添加: asyncio.TaskGroup.

可等待对象

我们说一个对象是可等待对象,如果它可以在 await 表达式中使用。许多 asyncio API 被设计为接受可等待对象。

主要有三种类型的可等待对象协程任务期货

协程

Python 协程是可等待对象,因此可以从其他协程中等待它们

import asyncio

async def nested():
    return 42

async def main():
    # Nothing happens if we just call "nested()".
    # A coroutine object is created but not awaited,
    # so it *won't run at all*.
    nested()

    # Let's do it differently now and await it:
    print(await nested())  # will print "42".

asyncio.run(main())

重要

在本文档中,术语“协程”可以用于两个密切相关的概念

  • 协程函数:一个 async def 函数;

  • 协程对象:调用协程函数返回的对象。

任务

任务用于并发地调度协程。

当使用诸如 asyncio.create_task() 之类的函数将协程包装到任务中时,协程会自动被调度尽快运行

import asyncio

async def nested():
    return 42

async def main():
    # Schedule nested() to run soon concurrently
    # with "main()".
    task = asyncio.create_task(nested())

    # "task" can now be used to cancel "nested()", or
    # can simply be awaited to wait until it is complete:
    await task

asyncio.run(main())

期货

一个 Future 是一个特殊的低级可等待对象,它表示异步操作的最终结果

等待一个期货对象时,这意味着协程将等待期货在其他地方被解析。

asyncio 中的期货对象是必要的,以便允许基于回调的代码与 async/await 一起使用。

通常,在应用程序级代码中不需要创建期货对象。

期货对象,有时由库和一些 asyncio API 公开,可以被等待

async def main():
    await function_that_returns_a_future_object()

    # this is also valid:
    await asyncio.gather(
        function_that_returns_a_future_object(),
        some_python_coroutine()
    )

返回期货对象的低级函数的一个很好的例子是 loop.run_in_executor()

创建任务

源代码: Lib/asyncio/tasks.py


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

coro 协程包装到一个 Task 中,并调度其执行。返回 Task 对象。

如果name 不是 None,则使用 Task.set_name() 将其设置为任务的名称。

可选的关键字参数context 允许为coro 运行指定一个自定义的 contextvars.Context。当没有提供context 时,会创建当前上下文的副本。

任务在由 get_running_loop() 返回的循环中执行,如果当前线程中没有正在运行的循环,则会引发 RuntimeError

注意

asyncio.TaskGroup.create_task() 是一个新的替代方案,它利用了结构化并发;它允许在具有强大安全保证的情况下等待一组相关任务。

重要

保存对该函数结果的引用,以避免任务在执行过程中消失。事件循环仅对任务保持弱引用。未在其他地方引用的任务可能在任何时候被垃圾回收,即使它尚未完成。对于可靠的“启动并忘记”后台任务,请将它们收集到一个集合中

background_tasks = set()

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

    # Add task to the set. This creates a strong reference.
    background_tasks.add(task)

    # To prevent keeping references to finished tasks forever,
    # make each task remove its own reference from the set after
    # completion:
    task.add_done_callback(background_tasks.discard)

在版本 3.7 中添加。

在版本 3.8 中更改: 添加了name 参数。

在版本 3.11 中更改: 添加了context 参数。

任务取消

任务可以轻松安全地取消。当任务被取消时,将在下一个机会在任务中引发 asyncio.CancelledError

建议协程使用 try/finally 块来稳健地执行清理逻辑。如果显式捕获了 asyncio.CancelledError,则通常应在清理完成后传播它。 asyncio.CancelledError 直接继承自 BaseException,因此大多数代码不需要了解它。

启用结构化并发的 asyncio 组件,例如 asyncio.TaskGroupasyncio.timeout(),是在内部使用取消实现的,如果协程吞没了 asyncio.CancelledError,则可能会出现故障。类似地,用户代码通常不应该调用 uncancel。但是,在确实需要抑制 asyncio.CancelledError 的情况下,还需要调用 uncancel() 来完全删除取消状态。

任务组

任务组将任务创建 API 与一种方便且可靠的方式结合起来,用于等待组中所有任务完成。

class asyncio.TaskGroup

一个 异步上下文管理器,用于保存一组任务。可以使用 create_task() 将任务添加到组中。当上下文管理器退出时,将等待所有任务。

在 3.11 版本中添加。

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

在这个任务组中创建一个任务。签名与 asyncio.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()}")

async with 语句将等待组中所有任务完成。在等待期间,仍然可以将新任务添加到组中(例如,通过将 tg 传递到其中一个协程中并在该协程中调用 tg.create_task())。一旦最后一个任务完成并且 async with 块退出,就不能再向组中添加新任务。

当组中任何任务第一次因除 asyncio.CancelledError 之外的异常而失败时,组中剩余的任务将被取消。之后不能再向组中添加任何任务。此时,如果 async with 语句的主体仍然处于活动状态(即,__aexit__() 尚未被调用),则直接包含 async with 语句的任务也会被取消。由此产生的 asyncio.CancelledError 将中断一个 await,但它不会从包含的 async with 语句中冒泡出来。

一旦所有任务都完成,如果任何任务因除 asyncio.CancelledError 之外的异常而失败,这些异常将被组合到一个 ExceptionGroupBaseExceptionGroup(根据情况而定;请参阅其文档),然后引发。

两个基本异常的处理方式有所不同:如果任何任务因 KeyboardInterruptSystemExit 而失败,任务组仍然会取消剩余的任务并等待它们,但随后会重新引发最初的 KeyboardInterruptSystemExit,而不是 ExceptionGroupBaseExceptionGroup.

如果 async with 语句的主体因异常而退出(因此 __aexit__() 被调用时设置了异常),则将其视为其中一个任务失败:取消剩余的任务,然后等待它们,并将非取消异常分组到一个异常组中并引发。传递到 __aexit__() 的异常(除非它是 asyncio.CancelledError)也被包含在异常组中。对于 KeyboardInterruptSystemExit,与上一段中一样,也进行了相同的特殊情况处理。

休眠

coroutine asyncio.sleep(delay, result=None)

阻塞 delay 秒。

如果提供了 result,则在协程完成时将其返回给调用者。

sleep() 始终挂起当前任务,允许其他任务运行。

将延迟设置为 0 提供了一个优化路径,允许其他任务运行。这可以通过长时间运行的函数来使用,以避免在函数调用期间阻塞事件循环。

协程示例,每秒显示当前日期 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 参数。

并发运行任务

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

并发运行 aws 序列中的 可等待对象

如果 aws 中的任何可等待对象是协程,它将自动作为任务调度。

如果所有可等待对象都成功完成,则结果将是返回值的聚合列表。结果值的顺序对应于 aws 中可等待对象的顺序。

如果 return_exceptionsFalse(默认值),则第一个引发的异常将立即传播到等待 gather() 的任务。aws 序列中的其他可等待对象不会被取消,并将继续运行。

如果 return_exceptionsTrue,则异常将与成功结果一样对待,并在结果列表中聚合。

如果 gather()取消,则所有提交的可等待对象(尚未完成的)也将被取消

如果 aws 序列中的任何任务或未来被取消,则将其视为引发了 CancelledError - 在这种情况下,gather() 调用不会被取消。这是为了防止一个提交的任务/未来的取消导致其他任务/未来的取消。

注意

创建和并发运行任务并等待它们完成的一种新替代方法是 asyncio.TaskGroupTaskGroup 为调度嵌套的子任务提供了比 gather 更强的安全保证:如果一个任务(或一个子任务,由一个任务调度的任务)引发异常,TaskGroup 将取消剩余的已调度任务,而 gather 则不会。

示例

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():
    # Schedule three calls *concurrently*:
    L = await asyncio.gather(
        factorial("A", 2),
        factorial("B", 3),
        factorial("C", 4),
    )
    print(L)

asyncio.run(main())

# Expected output:
#
#     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 为 false,则在 gather() 被标记为完成之后取消它不会取消任何提交的可等待对象。例如,gather 可以在将异常传播给调用者之后被标记为完成,因此,在从 gather 中捕获异常(由其中一个可等待对象引发)之后调用 gather.cancel() 不会取消任何其他可等待对象。

在 3.7 版本中更改: 如果 gather 本身被取消,则无论 return_exceptions 如何,取消都会被传播。

在 3.10 版本中更改: 删除了 loop 参数。

自 3.10 版本起已弃用: 如果未提供任何位置参数或并非所有位置参数都是类似未来的对象,并且没有正在运行的事件循环,则会发出弃用警告。

急切任务工厂

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

用于急切任务执行的任务工厂。

当使用此工厂(通过 loop.set_task_factory(asyncio.eager_task_factory))时,协程在 Task 构造期间同步开始执行。只有在协程阻塞时,任务才会被调度到事件循环中。这可能是一种性能改进,因为对于同步完成的协程,可以避免循环调度的开销。

一个常见的示例是协程使用缓存或记忆化来避免实际的 I/O(如果可能)。

注意

协程的立即执行是一个语义上的改变。如果协程返回或引发异常,任务将永远不会被调度到事件循环。如果协程执行阻塞,则任务将被调度到事件循环。此更改可能会对现有应用程序引入行为更改。例如,应用程序的任务执行顺序可能会发生变化。

在版本 3.12 中添加。

asyncio.create_eager_task_factory(custom_task_constructor)

创建一个急切的任务工厂,类似于 eager_task_factory(),在创建新任务时使用提供的 custom_task_constructor 而不是默认的 Task

custom_task_constructor 必须是一个可调用对象,其签名与 Task.__init__ 的签名匹配。该可调用对象必须返回一个与 asyncio.Task 兼容的对象。

此函数返回一个可调用对象,旨在通过 loop.set_task_factory(factory) 作为事件循环的任务工厂使用。

在版本 3.12 中添加。

屏蔽取消

awaitable asyncio.shield(aw)

保护一个 可等待对象 不被 cancelled

如果 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

重要

保存对传递给此函数的任务的引用,以避免任务在执行过程中消失。事件循环只保留对任务的弱引用。没有其他地方引用的任务可能会在任何时候被垃圾回收,甚至在它完成之前。

在 3.10 版本中更改: 删除了 loop 参数。

自版本 3.10 起弃用: 如果 aw 不是 Future 类对象并且没有正在运行的事件循环,则会发出弃用警告。

超时

asyncio.timeout(delay)

返回一个 异步上下文管理器,它可以用来限制等待某件事所花费的时间。

delay 可以是 None,也可以是等待的秒数(浮点数或整数)。如果 delayNone,则不会应用时间限制;如果在创建上下文管理器时延迟未知,这将很有用。

在任何情况下,都可以使用 Timeout.reschedule() 在创建后重新调度上下文管理器。

示例

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

如果 long_running_task 需要超过 10 秒才能完成,上下文管理器将取消当前任务并内部处理由此产生的 asyncio.CancelledError,将其转换为 TimeoutError,可以捕获和处理。

注意

asyncio.timeout() 上下文管理器是将 asyncio.CancelledError 转换为 TimeoutError 的东西,这意味着 TimeoutError 只能在上下文管理器外部捕获。

捕获 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.")

asyncio.timeout() 生成的上下文管理器可以重新调度到不同的截止日期并进行检查。

class asyncio.Timeout(when)

用于取消逾期协程的 异步上下文管理器

when 应该是一个绝对时间,表示上下文应该超时的时间,由事件循环的时钟测量

  • 如果 whenNone,则超时将永远不会触发。

  • 如果 when < loop.time(),则超时将在事件循环的下一次迭代中触发。

when() float | None

返回当前截止日期,如果当前截止日期未设置,则返回 None

reschedule(when: float | None)

重新调度超时。

expired() bool

返回上下文管理器是否已超过其截止日期(已过期)。

示例

async def main():
    try:
        # We do not know the timeout when starting, so we pass ``None``.
        async with asyncio.timeout(None) as cm:
            # We know the timeout now, so we reschedule it.
            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.")

超时上下文管理器可以安全地嵌套。

在 3.11 版本中添加。

asyncio.timeout_at(when)

类似于 asyncio.timeout(),除了 when 是停止等待的绝对时间,或者 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.")

在 3.11 版本中添加。

coroutine asyncio.wait_for(aw, timeout)

等待 aw 可等待对象 在超时的情况下完成。

如果 aw 是一个协程,它将自动被调度为一个任务。

timeout 可以是 None,也可以是等待的秒数(浮点数或整数)。如果 timeoutNone,则阻塞直到 future 完成。

如果发生超时,它将取消任务并引发 TimeoutError

为了避免任务 cancellation,将其包装在 shield() 中。

该函数将等待直到 future 实际被取消,因此总等待时间可能会超过 timeout。如果在取消过程中发生异常,它将被传播。

如果等待被取消,future aw 也将被取消。

示例

async def eternity():
    # Sleep for one hour
    await asyncio.sleep(3600)
    print('yay!')

async def main():
    # Wait for at most 1 second
    try:
        await asyncio.wait_for(eternity(), timeout=1.0)
    except TimeoutError:
        print('timeout!')

asyncio.run(main())

# Expected output:
#
#     timeout!

在版本 3.7 中更改: aw 由于超时而被取消时,wait_for 将等待 aw 被取消。以前,它会立即引发 TimeoutError

在 3.10 版本中更改: 删除了 loop 参数。

在版本 3.11 中更改: 引发 TimeoutError 而不是 asyncio.TimeoutError

等待原语

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

并发运行aws可迭代对象中的FutureTask实例,并阻塞直到满足return_when指定的条件。

aws可迭代对象不能为空。

返回两个任务/期货集:(done, pending)

用法

done, pending = await asyncio.wait(aws)

如果指定了timeout(浮点数或整数),则可用于控制等待的最大秒数。

请注意,此函数不会引发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 中的变更: 添加了对生成器生成任务的支持。

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

并发运行aws可迭代对象中的可等待对象。返回一个协程迭代器。返回的每个协程都可以被等待,以获取可迭代对象中剩余可等待对象的下一个最早结果。

如果在所有期货完成之前发生超时,则引发TimeoutError

示例

for coro in as_completed(aws):
    earliest_result = await coro
    # ...

在 3.10 版本中更改: 删除了 loop 参数。

版本 3.10 中已弃用: 如果aws可迭代对象中的所有可等待对象都不是类似期货的对象,并且没有正在运行的事件循环,则会发出弃用警告。

版本 3.12 中的变更: 添加了对生成器生成任务的支持。

在线程中运行

协程 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')}")
    # Note that time.sleep() can be replaced with any blocking
    # IO-bound operation, such as file operations.
    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())

# Expected output:
#
# 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(),我们可以在单独的线程中运行它,而不会阻塞事件循环。

注意

由于GILasyncio.to_thread()通常只能用于使 IO 绑定函数非阻塞。但是,对于释放 GIL 的扩展模块或没有 GIL 的替代 Python 实现,asyncio.to_thread()也可以用于 CPU 绑定函数。

在版本 3.9 中添加。

从其他线程调度

asyncio.run_coroutine_threadsafe(coro, loop)

将协程提交到给定的事件循环。线程安全。

返回一个concurrent.futures.Future,用于等待来自另一个操作系统线程的结果。

此函数旨在从与运行事件循环的线程不同的操作系统线程调用。示例

# 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) == 3

如果协程中引发异常,则返回的期货将被通知。它也可以用于取消事件循环中的任务

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参数。

在版本 3.5.1 中添加。

内省

asyncio.current_task(loop=None)

返回当前正在运行的Task实例,如果没有任何任务正在运行,则返回None

如果loopNone,则get_running_loop()用于获取当前循环。

在版本 3.7 中添加。

asyncio.all_tasks(loop=None)

返回循环运行的尚未完成的Task对象集。

如果loopNone,则get_running_loop()用于获取当前循环。

在版本 3.7 中添加。

asyncio.iscoroutine(obj)

如果obj是协程对象,则返回True

在版本 3.4 中添加。

任务对象

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

一个类似于 Future 的对象,用于运行 Python 协程。不是线程安全的。

任务用于在事件循环中运行协程。如果一个协程等待一个 Future,则 Task 会暂停协程的执行并等待 Future 完成。当 Future 完成时,包装的协程的执行将恢复。

事件循环使用协作调度:一个事件循环一次运行一个 Task。当一个 Task 等待一个 Future 完成时,事件循环会运行其他 Task、回调或执行 IO 操作。

使用高级的 asyncio.create_task() 函数创建 Task,或者使用低级的 loop.create_task()ensure_future() 函数。不建议手动实例化 Task。

要取消正在运行的 Task,请使用 cancel() 方法。调用它会导致 Task 向包装的协程抛出一个 CancelledError 异常。如果协程在取消期间正在等待一个 Future 对象,则 Future 对象将被取消。

cancelled() 可用于检查 Task 是否被取消。如果包装的协程没有抑制 CancelledError 异常,并且实际上被取消了,则该方法返回 True

asyncio.TaskFuture 继承了所有 API,除了 Future.set_result()Future.set_exception()

可选的关键字参数 context 允许为 coro 指定一个自定义的 contextvars.Context 以便在其中运行。如果没有提供 context,则 Task 会复制当前上下文,然后在复制的上下文中运行其协程。

可选的关键字参数 eager_start 允许在创建 Task 时立即开始执行 asyncio.Task。如果设置为 True 并且事件循环正在运行,则任务将立即开始执行协程,直到协程第一次阻塞。如果协程在没有阻塞的情况下返回或引发异常,则任务将立即完成,并将跳过调度到事件循环。

在版本 3.7 中变更: 添加了对 contextvars 模块的支持。

在版本 3.8 中更改: 添加了name 参数。

从版本 3.10 开始弃用: 如果未指定 loop 并且没有正在运行的事件循环,则会发出弃用警告。

在版本 3.11 中更改: 添加了context 参数。

在版本 3.12 中变更: 添加了 eager_start 参数。

done()

如果 Task 已完成,则返回 True

当包装的协程返回一个值、引发一个异常或 Task 被取消时,Task 就完成了。

result()

返回 Task 的结果。

如果 Task 已完成,则返回包装的协程的结果(或者如果协程引发了异常,则重新引发该异常)。

如果 Task 被取消了,则此方法会引发一个 CancelledError 异常。

如果 Task 的结果尚未可用,则此方法会引发一个 InvalidStateError 异常。

exception()

返回 Task 的异常。

如果包装的协程引发了异常,则返回该异常。如果包装的协程正常返回,则此方法返回 None

如果 Task 被取消了,则此方法会引发一个 CancelledError 异常。

如果 Task 尚未完成,则此方法会引发一个 InvalidStateError 异常。

add_done_callback(callback, *, context=None)

添加一个回调,在 Task 完成时运行。

此方法仅应在低级基于回调的代码中使用。

有关更多详细信息,请参阅 Future.add_done_callback() 的文档。

remove_done_callback(callback)

从回调列表中删除 callback

此方法仅应在低级基于回调的代码中使用。

有关更多详细信息,请参阅 Future.remove_done_callback() 的文档。

get_stack(*, limit=None)

返回此 Task 的堆栈帧列表。

如果包装的协程尚未完成,则返回其暂停位置的堆栈。如果协程已成功完成或被取消,则返回一个空列表。如果协程被异常终止,则返回跟踪帧列表。

帧始终按从旧到新的顺序排列。

对于暂停的协程,只返回一个堆栈帧。

可选的 limit 参数设置要返回的帧的最大数量;默认情况下返回所有可用帧。返回列表的排序方式取决于返回的是堆栈还是跟踪:返回堆栈的最新帧,但返回跟踪的最旧帧。(这与跟踪模块的行为一致。)

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

打印此 Task 的堆栈或跟踪。

这会生成类似于跟踪模块的输出,用于通过 get_stack() 检索的帧。

limit 参数直接传递给 get_stack()

file 参数是一个 I/O 流,输出将写入其中;默认情况下,输出将写入 sys.stdout

get_coro()

返回由 Task 包装的协程对象。

注意

对于已立即完成的任务,这将返回 None。请参阅 立即执行的任务工厂

在版本 3.8 中添加。

在版本 3.12 中变更: 新添加的立即执行任务意味着结果可能是 None

get_context()

返回与任务关联的 contextvars.Context 对象。

在版本 3.12 中添加。

get_name()

返回任务的名称。

如果任务没有显式分配名称,默认的 asyncio 任务实现会在实例化期间生成一个默认名称。

在版本 3.8 中添加。

set_name(value)

设置任务的名称。

value 参数可以是任何对象,然后将其转换为字符串。

在默认的任务实现中,名称将在任务对象的 repr() 输出中可见。

在版本 3.8 中添加。

cancel(msg=None)

请求取消任务。

这将安排在事件循环的下一个周期中,将 CancelledError 异常抛入包装的协程中。

然后,协程有机会清理,甚至可以通过使用 try … … except CancelledErrorfinally 块来抑制异常来拒绝请求。因此,与 Future.cancel() 不同,Task.cancel() 不能保证任务会被取消,尽管完全抑制取消并不常见,并且被积极地劝阻。如果协程仍然决定抑制取消,它需要调用 Task.uncancel() 以及捕获异常。

在版本 3.9 中更改: 添加了 msg 参数。

在版本 3.11 中更改: msg 参数从取消的任务传播到其等待者。

以下示例说明了协程如何拦截取消请求

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

    try:
        # Wait for 1 hour
        await asyncio.sleep(3600)
    except asyncio.CancelledError:
        print('cancel_me(): cancel sleep')
        raise
    finally:
        print('cancel_me(): after sleep')

async def main():
    # Create a "cancel_me" Task
    task = asyncio.create_task(cancel_me())

    # Wait for 1 second
    await asyncio.sleep(1)

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

asyncio.run(main())

# Expected output:
#
#     cancel_me(): before sleep
#     cancel_me(): cancel sleep
#     cancel_me(): after sleep
#     main(): cancel_me is cancelled now
cancelled()

如果任务已取消,则返回 True

当使用 cancel() 请求取消,并且包装的协程传播了抛入其中的 CancelledError 异常时,任务将取消

uncancel()

减少对该任务的取消请求计数。

返回剩余的取消请求数。

请注意,一旦取消的任务执行完成,对 uncancel() 的进一步调用将无效。

在 3.11 版本中添加。

此方法由 asyncio 的内部机制使用,预计不会被最终用户代码使用。特别是,如果任务成功取消,这将允许结构化并发元素(如 任务组asyncio.timeout())继续运行,将取消隔离到各自的结构化块中。例如

async def make_request_with_timeout():
    try:
        async with asyncio.timeout(1):
            # Structured block affected by the timeout:
            await make_request()
            await make_another_request()
    except TimeoutError:
        log("There was a timeout")
    # Outer code not affected by the timeout:
    await unrelated_code()

虽然包含 make_request()make_another_request() 的块可能会由于超时而被取消,但 unrelated_code() 应该即使在超时的情况下也继续运行。这是使用 uncancel() 实现的。 TaskGroup 上下文管理器以类似的方式使用 uncancel()

如果最终用户代码由于某种原因通过捕获 CancelledError 来抑制取消,它需要调用此方法来删除取消状态。

cancelling()

返回对该任务的挂起取消请求数,即对 cancel() 的调用次数减去 uncancel() 调用的次数。

请注意,如果此数字大于零,但任务仍在执行,cancelled() 仍将返回 False。这是因为此数字可以通过调用 uncancel() 来降低,如果取消请求降至零,这会导致任务最终不会被取消。

此方法由 asyncio 的内部机制使用,预计不会被最终用户代码使用。有关更多详细信息,请参阅 uncancel()

在 3.11 版本中添加。