等待取消异步任务的正确方法是什么?

问题描述 投票:0回答:1

cancel的文档听起来你通常应该传播 CancelledError 异常:

因此,与 Future.cancel() 不同,Task.cancel() 并不能保证 Task 将被取消,尽管完全抑制取消并不常见并且被积极劝阻。如果协程仍然决定抑制取消,除了捕获异常之外,它还需要调用 Task.uncancel() 。

但是,这两种检测取消的方法都是不可等待的:

cancelling
告诉您取消是否正在进行,
cancelled
告诉您取消是否已完成。因此,等待取消的明显方法是:

foo_task.cancel()
try:
    await foo_task
except asyncio.CancelledError:
    pass

即使在 SO 上,网上也有很多这样的例子。 但是文档警告您,如果您这样做,异步机器将“行为不当”

支持结构化并发的 asyncio 组件,例如 asyncio.TaskGroup 和 asyncio.timeout(),是在内部使用取消实现的,如果协程吞下 asyncio.CancelledError,则可能会出现错误行为

现在您可能想知道为什么要等到任务完全取消才阻塞。问题是 asyncio 事件循环仅创建对任务的弱引用,因此,如果您的类正在关闭(例如由于

cleanup
方法或
__aexit__
)并且您不等待您生成的每个任务,那么您可能会在任务仍在运行时拆掉唯一的强引用,然后 python 就会对你大喊大叫:

ERROR base_events.py:1771: Task was destroyed but it is pending!

因此,这似乎避免了我专门被迫做我不应该做的事情的错误:P 唯一的选择似乎是奇怪的非Pythonic hackery,比如将我所做的每个任务填充到全局集中并等待它们全部跑步结束。

python async-await task python-asyncio cancellation
1个回答
0
投票

您遇到清理问题。

with
语句一般用来解决清理问题。

使用

asyncio.TaskGroup

async with asyncio.TaskGroup() as tg:
    tg.create_task(some_coro(...)).cancel()
© www.soinside.com 2019 - 2024. All rights reserved.