python在create_task中出异常无错误日志的问题

问题现象

通过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()在返回结果时,如发现异常会自动抛出

发表于 2020年09月23日 17:15   评论:0   阅读:2046  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo