python异步程序的优雅退出

基于多线程的优雅退出比较常见,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()来返回结果和异常的。

发表于 2019年10月13日 17:18   评论:0   阅读:4481  



回到顶部

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