同时处理 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:
- 当子任务捕捉到 CancelledError 时,它将永远 运行 并导致内存泄漏。
- 在较大的项目中,哪个任务导致了这种情况并不明显。
处理2:
- 很明显子任务没有通过
await task
取消
- 但也有可能在调用
await task
的同时,任务handling2
本身也被取消了。
所以 handling2
本身会捕获 CancelledError
。
- 这真的很少见,但它发生在我的大型项目中。 (难以调试)
那么有没有另一种方法来处理取消任务并等待它们结束?
(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
设想以下 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:
- 当子任务捕捉到 CancelledError 时,它将永远 运行 并导致内存泄漏。
- 在较大的项目中,哪个任务导致了这种情况并不明显。
处理2:
- 很明显子任务没有通过
await task
取消
- 但也有可能在调用
await task
的同时,任务handling2
本身也被取消了。
所以handling2
本身会捕获CancelledError
。- 这真的很少见,但它发生在我的大型项目中。 (难以调试)
那么有没有另一种方法来处理取消任务并等待它们结束?
(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