Python似乎没有正确处理多线程程序中的异步信号。但是,我想我会在这里查看是否有人可以发现我违反某些原则或误解某些概念的地方。
我在SO上发现了类似的主题,但没有一个看起来完全相同。
场景是:我有两个线程,读者线程和编写器线程(主线程)。 writer线程写入读者线程轮询的管道。这两个线程使用threading.Event()
原语进行协调(我假设使用pthread_cond_wait
实现)。当读者线程最终设置它时,主线程在Event
上等待。
但是,如果我想在主线程等待Event
时中断我的程序,则不会异步处理KeyboardInterrupt。
这是一个小程序来说明我的观点:
#!/usr/bin/python
import os
import sys
import select
import time
import threading
pfd_r = -1
pfd_w = -1
reader_ready = threading.Event()
class Reader(threading.Thread):
"""Read data from pipe and echo to stdout."""
def run(self):
global pfd_r
while True:
if select.select([pfd_r], [], [], 1)[0] == [pfd_r]:
output = os.read(pfd_r, 1000)
sys.stdout.write("R> '%s'\n" % output)
sys.stdout.flush()
# Suppose there is some long-running processing happening:
time.sleep(10)
reader_ready.set()
# Set up pipe.
(pfd_r, pfd_w) = os.pipe()
rt = Reader()
rt.daemon = True
rt.start()
while True:
reader_ready.clear()
user_input = raw_input("> ").strip()
written = os.write(pfd_w, user_input)
assert written == len(user_input)
# Wait for reply -- Try to ^C here and it won't work immediately.
reader_ready.wait()
使用'./bug.py'启动程序并在提示符下输入一些输入。一旦您看到读者回复前缀为“R>”,请尝试使用^C
进行中断。
我看到的(Ubuntu Linux 10.10,Python 2.6.6)是在阻塞^C
返回之后才处理reader_ready.wait()
。我期望看到的是^C
是异步引发的,导致程序终止(因为我没有捕获KeyboardInterrupt)。
这似乎是一个人为的例子,但我在一个真实世界的程序中遇到了这个问题,time.sleep(10)
被实际计算所取代。
我做了一些明显错误的事情,比如误解了预期的结果会是什么?
编辑:我刚刚使用Python 3.1.1进行了测试,存在同样的问题。
答案 0 :(得分:1)
wait()
对象的threading._Event
方法实际上依赖于thread.lock
的{{1}}方法。但是,thread documentation表示锁定的acquire()
方法无法中断,并且在释放锁定后将处理任何acquire()
异常。
基本上,这是按预期工作的。实现此行为的线程对象依赖于某个点(包括队列)的锁定,因此您可能希望选择另一个路径。
答案 1 :(得分:0)
或者,您也可以使用signal模块的pause()
功能代替reader_ready.wait()
。 signal.pause()
是一个阻塞函数,当进程收到信号时会被解锁。在您的情况下,当按下^C
时,SIGINT信号将取消阻止该功能。
根据文档,该功能不适用于Windows。我已经在Linux上测试了它并且它有效。我认为这比使用wait()
超时更好。