同时处理 asyncio CancelledError

Handling asyncio CancelledError simultaneous

设想以下 python 代码取消子任务。
它不是真正的程序,但应该演示结构。

import asyncio

async def subtask():
    sleep = 99999
    while True:
        try:
            print("Very long task")
            await asyncio.sleep(sleep)
        except asyncio.CancelledError:
            print("Incorrectly caught error")
            sleep = 2  # make it visible that task is still running
            pass

async def handling1():
    for i in range(3):
        print("Start subtask")
        task = asyncio.create_task(subtask())
        await asyncio.sleep(1)
        task.cancel()
        print("Subtask canceled")

async def handling2():
    for i in range(3):
        print("Start subtask")
        task = asyncio.create_task(subtask())
        await asyncio.sleep(1)
        task.cancel()
        try:
            await task
        except asyncio.CancelledError:
            pass
        print("Subtask canceled")


asyncio.run(handling1())

asyncio.run(handling1()) 替换为 asyncio.run(handling2()) 以查看其他处理方式。

处理 1:

处理2:

那么有没有另一种方法来处理取消任务并等待它们结束?
subtask不是真实的例子,但应该演示一个错误抓取的CancelledError

BUG当然在subtask,调用.cancel()时应该取消了。

既然你已经知道,说它被错误地捕获,并且知道 handling1 是正确的解决方案但不适应这个错误,这里是对 handling2 的修复:

task = asyncio.create_task(subtask())
await asyncio.sleep(1)
task.cancel()
try:
    # Shield will make sure 2nd cancellation from wait_for will not block.
    await asyncio.wait_for(asyncio.shield(task), 1)
except asyncio.TimeoutError:
    if task.cancelled():  # Slight chance of happening
        return # Task was cancelled correctly after exactly 1 second.
    print("Task was not cancelled after 1 second! bug bug bug")
except asyncio.CancelledError:
    if task.cancelled():
        return  # Task was cancelled correctly
    print("handling 2 was cancelled!")
    raise