8

在多线程 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()

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

据推测,这是因为 SIGINT 被发送到主线程,主线程被挂起(等待 GIL),而分叉线程在控制台读取时阻塞。在 raw_input 返回后,主线程在获取 GIL 之前不会执行其信号处理程序。(如果我对此有误,请纠正我——我不是 Python 线程实现方面的专家。)

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

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


编辑:我错误地描述了上面的潜在问题。在进一步的调查中,阻止信号处理的是主线程的调用join():Guido van Rossum 自己已经解释过 join 中的底层锁获取是 uninterruptible。这意味着信号实际上被推迟到整个线程完成 - 所以这实际上与所有无关raw_input(只是后台线程阻塞以便连接没有完成的事实)。

4

2 回答 2

6

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

while my_thread.isAlive():
    my_thread.join(5.0)
于 2012-03-20T16:29:39.520 回答
1

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

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

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

于 2012-02-14T00:39:33.543 回答