为啥在异步事件循环运行时我无法捕获 SIGINT?

Posted

技术标签:

【中文标题】为啥在异步事件循环运行时我无法捕获 SIGINT?【英文标题】:Why can't I catch SIGINT when asyncio event loop is running?为什么在异步事件循环运行时我无法捕获 SIGINT? 【发布时间】:2014-07-16 07:42:51 【问题描述】:

在 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')

请参阅官方(郁金香)邮件列表上的discussion。

【问题讨论】:

python-tulip google group相关讨论 【参考方案1】:

我找到了一种解决方法,即安排定期回调。在此运行期间,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')

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

这件事已经在官方(郁金香)邮件列表上discussed,我的解决方法显然是目前要走的路。

更新

据说有一个修复程序 made its way into Python 3.5,所以希望我的解决方法会被那个 Python 版本淘汰。

【讨论】:

在 3.6.0 中遇到了同样的问题,所以这个 bug 仍然存在。【参考方案2】:

我发现在执行 asyncio 事件循环时,我的程序无法中断(即在终端中按 Ctrl+C)

澄清一下:ctrl-C 可能不起作用,但 ctrl-break 可以正常工作。

【讨论】:

【参考方案3】:

通常,您会使用 loop.add_signal_handler() 为这些添加回调,但很遗憾,内置 Windows 事件循环不支持此功能:/

可以使用定期检查,是的。否则,循环将超出signal 模块捕获信号的能力。

【讨论】:

看起来 select.select 因无限超时而阻塞,在此期间未处理 SIGINT。谷歌搜索表明它可能与 Python 捕获 Windows 信号有关,因此这些信号在 select.select 返回之前被忽略:/

以上是关于为啥在异步事件循环运行时我无法捕获 SIGINT?的主要内容,如果未能解决你的问题,请参考以下文章

SDL/C++ OpenGL 程序,如何阻止 SDL 捕获 SIGINT

为啥使用Try,Catch捕获异常,程序依然Crash

为啥我无法在具有 void 返回类型的异步函数中捕获异常?

在 KeyboardInterrupt 上关闭异步循环 - 运行停止例程

无法在消费者线程中使用 JNI 的生产者-消费者程序中捕获 SIGINT 信号

如何在 C# 控制台应用程序中捕获 ctrl-c (SIGINT)