协程 4 —— asyncio.Task 对象

相关链接:
1. 概念篇
2. 生成器协程、协程的演化
3. 事件循环(asyncio)核心原理
4. asyncio.Task 对象
5. asyncio库基础函数
6. 异步上下文管理器

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

  • 一个与 Future 类似 的对象,可运行 Python 协程。非线程安全。

  • Task 对象被用来在事件循环中运行协程。如果一个协程在等待一个 Future 对象,Task 对象会挂起该协程的执行并等待该 Future 对象完成。当该 Future 对象 完成,被打包的协程将恢复执行。

  • 事件循环使用协同日程调度: 一个事件循环每次运行一个 Task 对象。而一个 Task 对象会等待一个 Future 对象完成,该事件循环会运行其他 Task、回调或执行 IO 操作。

  • 使用高层级的 asyncio.create_task() 函数来创建 Task 对象,也可用低层级的 loop.create_task() 或 ensure_future() 函数。不建议手动实例化 Task 对象。

  • 要取消一个正在运行的 Task 对象可使用 cancel() 方法。调用此方法将使该 Task 对象抛出一个 CancelledError 异常给打包的协程。如果取消期间一个协程正在对 Future 对象执行 await,该 Future 对象也将被取消。

  • cancelled() 可被用来检测 Task 对象是否被取消。如果打包的协程没有抑制 CancelledError 异常并且确实被取消,该方法将返回 True。

  • asyncio.Task 从 Future 继承了其除 Future.set_result() 和 Future.set_exception() 以外的所有 API。

  • 可选的仅限关键字参数 context 允许指定自定义的 contextvars.Context 供 coro 运行。 如果未提供 context,Task 将拷贝当前上下文并随后在拷贝的上下文中运行其协程。

  • 可选的仅限关键字参数 eager_start 允许在任务创建时主动开始 asyncio.Task 的执行。 如果设为 True 并且事件循环正在运行,任务将立即开始执行协程,直到该协程第一次阻塞。 如果协程未发生阻塞即返回或引发异常,任务将主动结果并将跳过向事件循环添加计划任务。

方法1:done()

如果 Task 对象 已完成 则返回 True。

当 Task 所封包的协程返回一个值、引发一个异常或 Task 本身被取消时,则会被认为 已完成。

方法2:result()

返回 Task 的结果。
  • 如果 Task 对象 已完成,其封包的协程的结果会被返回 (或者当协程引发异常时,该异常会被重新引发。)

  • 如果 Task 对象 被取消,此方法会引发一个 CancelledError 异常。

  • 如果 Task 对象的结果还不可用,此方法会引发一个 InvalidStateError 异常。

方法3:exception()

返回 Task 对象的异常。
  • 如果所封包的协程引发了一个异常,该异常将被返回。如果所封包的协程正常返回则该方法将返回 None。

  • 如果 Task 对象 被取消,此方法会引发一个 CancelledError 异常。

  • 如果 Task 对象尚未 完成,此方法将引发一个 InvalidStateError 异常。

方法4:add_done_callback(callback, *, context=None)

  • 添加一个回调,将在 Task 对象 完成 时被运行。

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

  • 要了解更多细节请查看 Future.add_done_callback() 的文档。

方法5:remove_done_callback(callback)

  • 从回调列表中移除 callback 。

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

  • 要了解更多细节请查看 Future.remove_done_callback() 的文档。

方法6:get_stack(*, limit=None)

  • 返回此 Task 对象的栈框架列表。

  • 如果所封包的协程未完成,这将返回其挂起所在的栈。如果协程已成功完成或被取消,这将返回一个空列表。如果协程被一个异常终止,这将返回回溯框架列表。

  • 框架总是从按从旧到新排序。

  • 每个被挂起的协程只返回一个栈框架。

  • 可选的 limit 参数指定返回框架的数量上限;默认返回所有框架。返回列表的顺序要看是返回一个栈还是一个回溯:栈返回最新的框架,回溯返回最旧的框架。(这与 traceback 模块的行为保持一致。)

方法7:print_stack(*, limit=None, file=None)

  • 打印此 Task 对象的栈或回溯。

  • 此方法产生的输出类似于 traceback 模块通过 get_stack() 所获取的框架。

  • limit 参数会直接传递给 get_stack()。

  • file 参数是输出所写入的 I/O 流;在默认情况下输出会写入到 sys.stdout。

方法8:get_coro()

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

备注:这对于已经主动完成的任务将返回 None。

方法9:get_context()

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

方法10:get_name()

  • 返回 Task 的名称。

  • 如果没有一个 Task 名称被显式地赋值,默认的 asyncio Task 实现会在实例化期间生成一个默认名称。

方法11:set_name(value)

  • 设置 Task 的名称。

  • value 参数可以为任意对象,它随后会被转换为字符串。

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

方法12:cancel(msg=None)

  • 请求取消 Task 对象。

  • 这将安排在下一轮事件循环中抛出一个 CancelledError 异常给被封包的协程。

协程随后将有机会进行清理甚至通过 try … … except CancelledError … finally 代码块抑制异常来拒绝请求。 因此,不同于 Future.cancel(), Task.cancel() 不保证 Task 会被取消,虽然完全抑制撤销并不常见也很不建议这样做。 但是如果协程决定要抑制撤销,那么它需要额外调用 Task.uncancel() 来捕获异常。

以下示例演示了协程是如何侦听取消请求的:

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 = 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

方法13:cancelled()

如果 Task 对象 被取消 则返回 True。

当使用 cancel() 发出取消请求时 Task 会被 取消,其封包的协程将传播被抛入的 CancelledError 异常。

方法14:uncancel()

  • 递减对此任务的取消请求计数。

  • 返回剩余的取消请求数量。

  • 请注意一理被取消的任务执行完成,继续调用 uncancel() 将是低效的。

此方法是供 asyncio 内部使用而不应被最终用户代码所使用。 特别地,在一个 Task 成功地保持未取消状态的时候使用,这可以允许结构化的并发元素如 任务组 和 asyncio.timeout() 继续运行,将取消操作隔离在相应的结构化代码块中。

例如:

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()
  • 带有 make_request() 和 make_another_request() 的代码块可能因超时而被取消,而 unrelated_code() 应当在超时的情况下继续运行。 这是通过 uncancel() 实现的。 TaskGroup 上下文管理器也会以类似的方式来使用 uncancel()。

  • 如果最终用户代码出于某种原因通过捕获 CancelledError 抑制撤销操作,那么它需要调用此方法来移除撤销状态。

  • 当该方法将取消计数递减至零,该方法会检查之前的 cancel() 调用是否已安排将 CancelledError 抛出到任务中。 如果尚未抛出,则该安排将被撤销(通过重置内部的 _must_cancel 旗标 )。

方法15:cancelling()

  • 返回对此 Task 的挂起请求次数,即对 cancel() 的调用次数减去 uncancel() 的调用次数。

  • 请注意如果该数字大于零但相应 Task 仍在执行,cancelled() 仍将返回 False。 这是因此该数字可通过调用 uncancel() 来减少,这会导致任务在取消请求降到零时尚未被取消。

  • 此方法是供 asyncio 内部使用而不应被最终用户代码所使用。 请参阅 uncancel() 了解详情。


<<上一篇介绍 《事件循环(asyncio)核心原理》
下一篇介绍 《asyncio库基础函数》>>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值