使用^ C/KeyboardInterrupt在子线程中中断Python raw_input()

adr*_*ian 8 python input sigint control-c python-multithreading

在多线程Python程序中,一个线程有时会使用内置的raw_input()来请求控制台输入.我希望能够在raw_input提示符下通过在shell中键入^ C来关闭程序(即,使用SIGINT信号).但是,当子线程正在执行raw_input时,键入^ C什么也不做 - 在我点击返回(离开raw_input)之前不会引发KeyboardInterrupt.

例如,在以下程序中:

import threading

class T(threading.Thread):
    def run(self):
        x = raw_input()
        print x

if __name__ == '__main__':
    t = T()
    t.start()
    t.join()
Run Code Online (Sandbox Code Playgroud)

键入^ C在输入完成之前不会执行任何操作.但是,如果我们只是调用T().run()(即,单线程情况:只在主线程中运行raw_input),^ C立即关闭程序.

据推测,这是因为SIGINT被发送到主线程,它被挂起(等待GIL),而分支线程在控制台上阻塞读取.主线程在raw_input返回后抓取GIL之前不会执行其信号处理程序.(如果我错了,请纠正我 - 我不是Python线程实现方面的专家.)

有没有办法以类似raw_input的方式从stdin读取,同时允许主线程处理SIGINT,从而降低整个过程?

[我在Mac OS X和一些不同的Linux上观察到了上述行为.]


编辑:我错误地描述了上面的根本问题.在进一步调查中,主要线程的呼吁join()是阻止信号处理:Guido van Rossum本人已经解释说,加入的底层锁定是不可中断的.这意味着信号实际上被推迟到整个线程完成 - 所以这实际上完全没有任何关系raw_input(只是后台线程阻塞以便连接没有完成的事实).

amc*_*abb 6

当调用join时没有超时,它是不可中断的,但是当它被超时调用时,它是可中断的.尝试添加任意超时并将其置于while循环中:

while my_thread.isAlive():
    my_thread.join(5.0)
Run Code Online (Sandbox Code Playgroud)


Irf*_*rfy 1

确实没有简单的方法可以解决这个问题。

一种方法是重新组织和分解代码,使需要可Ctrl-C中断性的函数部分在主线程上执行。您可以使用队列发送执行请求,同样也可以发送结果值。主线程需要一个输入队列,每个非主线程需要一个输出队列;和协调的主线程退出。显然,这种方式在任何给定时间只执行一个阻塞函数,这可能不是您想要的。

下面是这个想法的一个工作示例,其中稍微不正当地使用信号量来协调主线程退出。