为什么这个Python异步脚本永远不会完成?

时间:2017-04-07 16:19:22

标签: python python-3.x python-asyncio

我创建了一个MCVE示例,其中包含了我正在处理的更大的代码库,所以有些东西看起来是一种时髦的做事方式,但它们只是最小的版本,所以并非所有事情都有意义为什么我是这样做的。我已经知道了一些解决方法,而且在这一点上,我大多只是好奇为什么我会看到这种行为。

我有以下脚本,它具有等待未来的异步功能。然后该脚本拦截信号以设置未来结果:

import asyncio
import time
import signal

f = asyncio.Future()
loop = asyncio.get_event_loop()

async def wait_till_signal():
    await f

def send_stop():
    print('stopping', time.time())
    f.set_result(True)
    print('sent')
    print(f.result())

def handle_signal(s, a):
    print('sending stop', time.time())
    send_stop()

signal.signal(signal.SIGINT, handle_signal)

loop.run_until_complete(wait_till_signal())

此脚本正确获取中断,并且似乎正确设置了未来,但由于某种原因,脚本永远不会终止。

要为您重现,只需运行脚本,然后按ctrl + c。出于某种原因,它永远不会停止。

现在这里变得奇怪了。如果将以下内容添加到脚本的顶部(在定义循环之后),则脚本会停止正常。

async def do_nothing_useful():
     for i in range(30):
         await asyncio.sleep(1)

loop.create_task(do_nothing_useful())

为什么协程在第一种情况下没有获得未来,但在第二种情况下它是否正确?

另外,另一个奇怪的事情是,如果将send_stop函数设置为异步,并将其添加为任务,则永远不会调用它。 (这遵循与上面相同的行为。如果do_nothing_useful()函数在循环中,一切正常,但没有它,它不会)

以下是永远不会调用send_stop的版本:

import asyncio
import time
import signal

f = asyncio.Future()

async def wait_till_signal():
    await f

async def send_stop():
    # this is async only because we are trying to try out crazy things
    print('stopping', time.time())
    f.set_result(True)
    print('sent')
    print(f.result())


def handle_signal(s, a):
    print('sending stop', time.time())
    loop.create_task(send_stop())


signal.signal(signal.SIGINT, handle_signal)

loop = asyncio.get_event_loop()
loop.run_until_complete(wait_till_signal())
print('done')

并且脚本永远不会打印stopping

我在linux上的python 3.5.3和3.6上试过这个

1 个答案:

答案 0 :(得分:3)

添加唤醒循环的信号处理程序的正确方法是使用loop.add_signal_handler。这将确保select()醒来处理信号。