为什么在运行asyncio事件循环时无法捕获SIGINT信号?

17

在Windows上使用Python 3.4.1时,我发现在执行异步事件循环(asyncio event loop)时,无法通过在终端中按Ctrl+C来中断程序。更确切地说,SIGINT信号被忽略了。相反,在不处于事件循环中时,我已确定SIGINT得到处理。

为什么在执行异步事件循环时会忽略SIGINT?

以下程序应该演示了问题-在终端中运行它,并尝试通过按Ctrl+C来停止它,它应该会继续运行:

import asyncio
import signal


# Never gets called after entering event loop
def handler(*args):
    print('Signaled')


signal.signal(signal.SIGINT, handler)

print('Event loop starting')
loop = asyncio.SelectorEventLoop()
asyncio.set_event_loop(loop)
loop.run_forever()
print('Event loop ended')

查看官方(Tulip)邮件列表中有关讨论的内容。


1
有关 python-tulip google group 的讨论。 - jfs
3个回答

15

我找到了一个解决方法,那就是安排一个定期回调。在这个回调正在运行时,显然会处理SIGINT信号:

import asyncio


def wakeup():
    # Call again
    loop.call_later(0.1, wakeup)


print('Event loop starting')
loop = asyncio.SelectorEventLoop()
# Register periodic callback
loop.call_later(0.1, wakeup)
asyncio.set_event_loop(loop)
loop.run_forever()
print('Event loop ended')

不确定为什么需要这样做,但它表明在事件循环等待事件(“轮询”)时信号被阻塞。

这个问题已经在官方的(Tulip)邮件列表上讨论过了,我的解决方法显然是目前的最佳选择。

更新

据说修复已经进入Python 3.5,所以希望我的解决方法将被该Python版本淘汰。


7
在3.6.0中遇到了同样的问题,因此这个 bug 仍然存在。 - tkerwin

6
我发现在执行asyncio事件循环时,我的程序无法被中断(即在终端中按Ctrl + C)。
澄清一下:Ctrl + C可能不起作用,但Ctrl + Break可以正常工作。

3

通常情况下,您可以使用loop.add_signal_handler()为它们添加回调函数,但很遗憾,在内置的Windows事件循环中不支持此功能 :/

可以使用定期检查。否则,循环将在signal模块无法捕获信号的情况下运行。


看起来 select.select 正在使用无限超时阻塞,而在此期间 SIGINT 没有被处理。谷歌搜索表明这可能与 Python 捕获 Windows 信号有关,因此这些信号将被忽略,直到 select.select 返回 :/ - aknuds1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接