VPf*_*PfB 4 python python-asyncio
为什么CancelledError没有在这个例子中被捕获?
import asyncio
q = asyncio.Queue()
async def getter():
try:
v = await q.get()
print(f"getter got {v}")
except asyncio.CancelledError:
print("getter cancelled")
async def test():
task = asyncio.ensure_future(getter())
task.cancel()
await task
def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
我想要获取"getter cancelled"消息,但收到了堆栈跟踪:
Traceback (most recent call last):
File "ce.py", line 22, in
main()
File "ce.py", line 19, in main
loop.run_until_complete(test())
File "/usr/lib64/python3.6/asyncio/base_events.py", line 468, in run_until_complete
return future.result()
concurrent.futures._base.CancelledError
Task.cancel声明:
这将安排CancelledError通过事件循环在下一个循环中抛出到包装的协同程序中.然后协程有机会使用try/except/finally清理甚至拒绝请求.
问题是getter甚至没有开始执行,您可以通过在开头添加打印来确认.由于try从未输入过块,因此except也没有运行.
发生这种情况是因为,与之相反await,ensure_future它不会立即开始执行协程,它只是将其调度为在下一个事件循环迭代中运行,就像call_soon普通函数一样.由于您立即取消了该任务,因此它将从可运行集中删除,并且其协程将在未启动的情况下关闭.
添加await asyncio.sleep(0)之前task.cancel(),您应该观察您期望的行为.我怀疑你不需要在你的实际代码中做出这样的改变 - 在不太可能的情况下,任务在它运行之前被取消,就像在例子中一样,它将没有机会获得尝试/除了清理的资源在第一位.
两条切线评论:
您可能希望asyncio.CancelledError在处理之后重新加注,否则它将被抑制.这不是问题getter所示的问题,但如果代码被隐藏在函数调用中,则可能是一个问题.更好的是,考虑使用finally或with传播异常并确保在不考虑异常类型的情况下释放资源.
当你需要创建一个任务,并运行一个协程,loop.create_task是首选到asyncio.ensure_future.简而言之,虽然两者对协同程序都做同样的事情,create_task但意图更清晰; ensure_future旨在接受更广泛的对象并获得未指定类型的未来.