Python asyncio程序不会退出

时间:2016-09-06 14:50:29

标签: python python-asyncio

我有一个带有两个asyncio任务的asyncio / Python程序:

  • 崩溃的人
  • 永远持续的。

我希望我的整个程序在第一次崩溃后退出。我不能让它发生。

import asyncio
import time

def infinite_while():
    while True:
        time.sleep(1)


async def task_1():
    await asyncio.sleep(1)
    assert False


async def task_2():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, lambda: infinite_while())


loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)

tasks = asyncio.gather(task_2(), task_1())
try:
    loop.run_until_complete(tasks)
except (Exception, KeyboardInterrupt) as e:
    print('ERROR', str(e))
    exit()

它打印ERROR但不退出。手动关闭时,程序将打印以下堆栈跟踪:

Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python3.5/concurrent/futures/thread.py", line 39, in _python_exit
    t.join()
  File "/usr/lib/python3.5/threading.py", line 1054, in join
    self._wait_for_tstate_lock()
  File "/usr/lib/python3.5/threading.py", line 1070, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt

1 个答案:

答案 0 :(得分:3)

当任务中出现异常时,它永远不会传播到通过eventloop启动任务的范围,即loop.run_until_complete(tasks)调用。想想看,就好像只在你的任务的上下文中抛出异常,这是你有机会处理它的顶级范围,否则它会在"背景&#中上升34。

这就是说,你永远不会从这个任务中抓住Exception

try:
    loop.run_until_complete(tasks)
except (Exception, KeyboardInterrupt) as e:
    print('ERROR', str(e))
    exit()

......这就是事件循环的工作原理。想象一下,如果你有一个服务有几个任务,其中一个会失败,那将停止整个服务。

当您在task1中捕获异常时,您可以手动stop事件循环,例如

async def task_1():
    await asyncio.sleep(1)
    try:
        assert False
    except Exception:
        # get the eventloop reference somehow
        eventloop.stop()

然而,这非常脏,而且有点hacky,所以我宁愿建议使用@D-Von suggested的解决方案,它更清洁,更安全。