使用 ^C/KeyboardInterrupt 中断子线程中的 Python raw_input()
在多线程 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 的方式从 stdin 读取数据,同时允许主线程处理 SIGINT从而降低整个过程?
[我在 Mac OS X 和一些不同的 Linux 上观察到了上述行为。]
编辑:我错误地描述了上述潜在问题。经过进一步调查,主线程对 join() 的调用阻止了信号处理:Guido van Rossum 本人已解释过 join 中的底层锁获取是不间断的。这意味着信号实际上被推迟到整个线程完成为止——所以这实际上与 raw_input 完全无关(只是后台线程正在阻塞,因此连接不会发生)完全的)。
In a multithreaded Python program, one thread sometimes asks for console input using the built-in raw_input(). I'd like to be able to be able to close the program while at a raw_input prompt by typing ^C at the shell (i.e., with a SIGINT signal). However, when the child thread is executing raw_input, typing ^C does nothing -- the KeyboardInterrupt is not raised until I hit return (leaving raw_input).
For example, in the following program:
import threading
class T(threading.Thread):
def run(self):
x = raw_input()
print x
if __name__ == '__main__':
t = T()
t.start()
t.join()
Typing ^C does nothing until after the input is finished. However, if we just call T().run()
(i.e., the single-threaded case: just run raw_input in the main thread), ^C closes the program immediately.
Presumably, this is because SIGINT is sent to the main thread, which is suspended (waiting for the GIL) while the forked thread blocks on the console read. The main thread does not get to execute its signal handler until it grabs the GIL after raw_input returns. (Please correct me if I'm wrong about this -- I'm not an expert on Python's threading implementation.)
Is there a way to read from stdin in a raw_input-like way while allowing the SIGINT to be handled by the main thread and thus bring down the whole process?
[I've observed the behavior above on Mac OS X and a few different Linuxes.]
Edit: I've mischaracterized the underlying problem above. On further investigation, it's the main thread's call to join()
that's preventing signal handling: Guido van Rossum himself has explained that the underlying lock acquire in join is uninterruptible. This means that the signal is actually being deferred until the entire thread finishes -- so this really has nothing to do with raw_input
at all (just the fact that the background thread is blocking so that the join does not complete).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当 join 被调用时没有超时,它是不可中断的,但是当它被调用时有超时时,它是可中断的。尝试添加任意超时并将其放入 while 循环中:
When join is called with no timeout, it is uninterruptable, but when it is called with a timeout, it is interruptable. Try adding an arbitrary timeout and putting it in a while loop:
确实没有简单的方法可以解决这个问题。
一种方法是重新组织和分解代码,使需要 Ctrl-C 可中断性的函数部分在主线程上执行。您可以使用队列发送执行请求,同样也可以发送结果值。主线程需要一个输入队列,每个非主线程需要一个输出队列;和协调的主线程退出。显然,这种方式在任何给定时间只执行一个阻塞函数,这可能不是您想要的。
这是这个想法的一个工作示例,其中稍微不正当地使用信号量来协调主线程退出。
There is really no easy way around this, period.
One approach is to reorganize and break up your code in a way that parts of functions which need Ctrl-C interruptibility are executed on the main thread. You use queues to send execution requests and likewise for the result values. You need one input queue for the main thread, and one output queue per non-main thread; and a coordinated main thread exit. Obviously, only one blocking function is executed at any given time this way, which may not be what you want.
Here's a working example of this idea with slightly perverse use of semaphores for the coordinated main thread exit.