协程和任务¶
本节概述了用于处理协程和任务的高级 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()
函数用于将协程并发地运行为 asyncioTasks
。让我们修改上面的示例,并并发地运行两个
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())
任务
任务用于并发地调度协程。
当使用诸如 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.TaskGroup
和 asyncio.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
之外的异常而失败,这些异常将被组合到一个 ExceptionGroup
或 BaseExceptionGroup
(根据情况而定;请参阅其文档),然后引发。
两个基本异常的处理方式有所不同:如果任何任务因 KeyboardInterrupt
或 SystemExit
而失败,任务组仍然会取消剩余的任务并等待它们,但随后会重新引发最初的 KeyboardInterrupt
或 SystemExit
,而不是 ExceptionGroup
或 BaseExceptionGroup
.
如果 async with
语句的主体因异常而退出(因此 __aexit__()
被调用时设置了异常),则将其视为其中一个任务失败:取消剩余的任务,然后等待它们,并将非取消异常分组到一个异常组中并引发。传递到 __aexit__()
的异常(除非它是 asyncio.CancelledError
)也被包含在异常组中。对于 KeyboardInterrupt
和 SystemExit
,与上一段中一样,也进行了相同的特殊情况处理。
休眠¶
- 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_exceptions 为
False
(默认值),则第一个引发的异常将立即传播到等待gather()
的任务。aws 序列中的其他可等待对象不会被取消,并将继续运行。如果 return_exceptions 为
True
,则异常将与成功结果一样对待,并在结果列表中聚合。如果
gather()
被取消,则所有提交的可等待对象(尚未完成的)也将被取消。如果 aws 序列中的任何任务或未来被取消,则将其视为引发了
CancelledError
- 在这种情况下,gather()
调用不会被取消。这是为了防止一个提交的任务/未来的取消导致其他任务/未来的取消。注意
创建和并发运行任务并等待它们完成的一种新替代方法是
asyncio.TaskGroup
。TaskGroup 为调度嵌套的子任务提供了比 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)¶
-
如果 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
,也可以是等待的秒数(浮点数或整数)。如果 delay 是None
,则不会应用时间限制;如果在创建上下文管理器时延迟未知,这将很有用。在任何情况下,都可以使用
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
应该是一个绝对时间,表示上下文应该超时的时间,由事件循环的时钟测量如果
when
是None
,则超时将永远不会触发。如果
when < loop.time()
,则超时将在事件循环的下一次迭代中触发。
示例
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
,也可以是等待的秒数(浮点数或整数)。如果 timeout 是None
,则阻塞直到 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可迭代对象中的
Future
和Task
实例,并阻塞直到满足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()
,我们可以在单独的线程中运行它,而不会阻塞事件循环。注意
由于GIL,
asyncio.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
。如果loop为
None
,则get_running_loop()
用于获取当前循环。在版本 3.7 中添加。
- asyncio.all_tasks(loop=None)¶
返回循环运行的尚未完成的
Task
对象集。如果loop为
None
,则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.Task
从Future
继承了所有 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 CancelledError
…finally
块来抑制异常来拒绝请求。因此,与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 版本中添加。