【问题标题】:Interrupting Python raw_input() in a child thread with ^C/KeyboardInterrupt使用 ^C/KeyboardInterrupt 在子线程中中断 Python raw_input()
【发布时间】:2012-02-13 22:59:39
【问题描述】:

在多线程 Python 程序中,一个线程有时会使用内置的 raw_input() 请求控制台输入。我希望能够通过在 shell 中键入 ^C(即使用 SIGINT 信号)在 raw_input 提示符下关闭程序。但是,当子线程正在执行 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 本人已经解释过the underlying lock acquire in join is uninterruptible。这意味着信号实际上被推迟到整个线程完成 - 所以这实际上与 raw_input 完全无关(只是后台线程正在阻塞,因此连接没有完成)。

【问题讨论】:

  • 一个不简单的结合线程和中断... [boromir.jpg]

标签: python input python-multithreading sigint control-c


【解决方案1】:

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

while my_thread.isAlive():
    my_thread.join(5.0)

【讨论】:

  • 只是一个小提示:线程需要是daemon 线程才能终止使用这种方法。 (在交互式解释器中测试时请注意:official 主线程是解释器本身,并且该线程不会在您的 main 代码结束时退出,因此即使是daemon 线程 -- 除非您在 main 末尾执行类似 sys.exit 的操作以强制解释器终止。这不适用于作为脚本执行时)
【解决方案2】:

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

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

Here's a working example of this idea 稍微不恰当地使用信号量来协调主线程退出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-22
    相关资源
    最近更新 更多