通过create_task()创建一个异步的协程,然而在协程中出现异常,居然没有任何错误日志打印!
当一个task被析构释放,或者被await的时候,python才知道是否任务完成以及是否有异常。所以当task一直不释放,而且也不await,出错也是没有日志的。
举例一:
class MyTasks:
def __init__(self):
self.task = None
async def main(self):
self.task = asyncio.create_task(self.hello())
async def hello(self):
await asyncio.sleep(3)
raise ValueError
async def main():
await MyTasks().main()
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
asyncio.get_event_loop().run_forever()
如果asyncio.create_task()的结果不存储到self.task里,则出现异常就会有错误日志,因为临时对象在资源回收时会自动析构。这里的self.task因为从来没有被await,MyTasks()产生的对象就不会释放,这样导致task也不会被释放,所以异常没有时机被处理,所以也不会有错误日志打印出来了。只有当程序整个结束时,才会因为强制内存回收被处理。
举例二:
async def cb():
print('cb running')
while True:
await asyncio.sleep(3)
print(100/0)
tt = None
def run():
global tt
tt = asyncio.create_task(cb())
也是一样道理,因为 tt为全局变量,程序不退出,就不会自动释放,这样出现的异常,就没有一个明确的时机进行处理。
当使用asyncio.create_task()创建一个异步任务时,而且不打算await该任务时,有几种写法来处理异常:
1. 不存储返回的task结果,在task自动析构时,自动处理异常(通常就是终端打印)。此方法不适合需要task.cancel()取消任务的情况。
2. 在任务函数中,使用try ... catch 自己捕获异常,然后自己打印到日志里
3. 给任务添加回调函数,task.add_done_callback(lambda f: f.result()),f.result()在返回结果时,如发现异常会自动抛出