【问题标题】:Awaiting a asyncio Future after Cancelling it取消后等待 asyncio Future
【发布时间】:2021-10-07 20:08:05
【问题描述】:

看着asyncio docs,我遇到了这个例子

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

task.cancel()之后,做await task的目的是什么?如果它曾经被屏蔽取消,这是等待未来完成吗?

换句话说,为什么不呢:

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

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

    task.cancel()

asyncio.run(main())

【问题讨论】:

  • 重点是任务已经取消,不能再等待了。示例代码将打印错误。屏蔽任务将继续运行,但也会抛出 CancelledError。

标签: python python-3.x python-asyncio future


【解决方案1】:

来自cancel()的文档(asyncio.Task下):

这会安排在事件循环的下一个循环中将CancelledError 异常抛出到包装的协程中。

然后,协程就有机会通过使用 tryexcept CancelledErrorfinally 块抑制异常来清理甚至拒绝请求。

asyncio.CancelledError 被扔进cancel_me() 时,在except asyncio.CancelledError 块中继续执行。对于文档中提供的sn-p,取消后cancel_me()是否等待其实并没有什么区别,因为异常处理块是同步执行的。

另一方面,如果异常处理块确实执行了异步操作,那么差异就会变得明显:

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

    try:
        # Wait for 1 hour
        await asyncio.sleep(3600)
    except asyncio.CancelledError:
        await asyncio.sleep(1)
        print('cancel_me(): cancel sleep, this never gets printed')
        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()
    print("main(): cancel_me is cancelled now")

asyncio.run(main())

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

由于以下原因,最后一次令人惊讶的打印发生了:

  • main() 在最后一次打印后返回
  • asyncio.run() 尝试取消所有待处理的任务
  • cancel_me(),虽然已经取消,但仍处于挂起状态,等待异常块休眠
  • cancel_me() 中的finally 子句被执行,偶数循环终止

另外值得注意的是:鉴于asyncio.run()CancelledError 抛出到所有仍待处理的任务中,如果cancel_me() 尚未被取消,except asyncio.CancelledError 块将完整执行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    • 2021-05-05
    • 2021-12-29
    • 2013-04-21
    • 2020-09-29
    相关资源
    最近更新 更多