基于多线程的优雅退出比较常见,thread_join这种就是等每个线程退出,再主线程退出。基于asyncio的异步程序如何优雅退出呢?
我们可以在退出之前查看loop当前还在监听的所有task,然后再run_until_complete(),例子如下:
if __name__ == '__main__':
coro = run()
loop = asyncio.get_event_loop()
loop.create_task(coro)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
try:
tasks = asyncio.Task.all_tasks()
if len(tasks):
print('still running task:', len(tasks))
loop.run_until_complete(asyncio.wait_for(asyncio.gather(*tasks), timeout=3))
loop.close()
except Exception as exc:
print(exc)
print('stopped')
这里使用asyncio.wait_for来控制最多等待3秒,就算没有执行完,也不再等待。
特别说一下asyncio.wait_for(),它可以wait_for coroutine,也可以wait_for future。不过在wait_for coroutine过程中,如果出现异常退出,会导致一个task永远等待,强制退出会出现『loop stopped before future completed』。些时需要使用到asyncio.shield(),当异常退出时,自动cancel掉task,代码举例:
async def do_sth():
print('do sth')
await asyncio.sleep(1)
raise Exception("error occur")
async def run():
try:
res = await asyncio.wait_for(asyncio.shield(do_sth()), 3)
except asyncio.TimeoutError:
print('timeout')
except Exception as e:
print('error', e)
而如果是wait_for future,就没有必要使用asyncio.shield(),估计是因为future是通过future.set_result()和future.set_exception()来返回结果和异常的。