最近遇到一个很诡异的问题,就是在网络连接断开后,cancel了future,但是等待进程没有后续了,也没有超时,也没有异常。按官方文档来看,如果await的future被cancel了,等待的协程会报CancelledError异常才对。
最小化例子代码为:
import asyncio
async def foo(future):
await asyncio.sleep(3)
future.cancel()
async def bar(future):
await future
print("hi")
loop = asyncio.get_event_loop()
future = loop.create_future()
loop.create_task(bar(future))
loop.create_task(foo(future))
loop.run_forever()
上面的例子中,hi不会被输出,但同时也不会有Exception堆栈打印出来,原因就在于loop.create_task()不会对返回值做处理,无论是异常返回还是正常返回。
三种处理方法:
1. 自行catch,以确保异常被捕获
async def bar(future):
try:
await future
except asyncio.CancelledError:
pass
print("hi")
2. 使用task的结束回调,查询结果
task = loop.create_task(bar(future))
task.add_done_callback(lambda x: print('cannelled: %s, done: %s' % (x.cancelled(), x.done())))
3. 使用关注运行结果的函数run_until_complete()
loop = asyncio.get_event_loop()
future = loop.create_future()
loop.create_task(foo(future))
loop.run_until_complete(bar(future))