Python multiprocessing.Event.wait在被信号中断时挂起
我使用以下代码来处理SIGINT事件。代码将multiprocessing.event设置为“唤醒”正在等待的主线程Python multiprocessing.Event.wait在被信号中断时挂起,python,python-multithreading,Python,Python Multithreading,我使用以下代码来处理SIGINT事件。代码将multiprocessing.event设置为“唤醒”正在等待的主线程 import multiprocessing import signal class Class1(object): _stop_event = multiprocessing.Event() @staticmethod def interrupt(): Class1._stop_event.set() def wait(se
import multiprocessing
import signal
class Class1(object):
_stop_event = multiprocessing.Event()
@staticmethod
def interrupt():
Class1._stop_event.set()
def wait(self):
print("Waiting for _stop_event")
if Class1._stop_event.wait(5):
print("_stop_event set.")
else:
print("Timed out.")
def stop(signum, frame):
print("Received SIG")
Class1.interrupt()
signal.signal(signal.SIGINT, stop)
c = Class1()
c.wait()
如果没有任何信号,等待方法将在10秒后超时,进程将按预期以以下输出退出:
Waiting for _stop_event
Timed out.
发送SIGINT信号时,会处理该信号,但event.wait方法不会立即返回,也不会在超时后返回。这个过程永远不存在。输出为:
Waiting for _stop_event
^CReceived SIG
Waiting for _stop_event
^CReceived SIG
^CReceived SIG
^CReceived SIG
^CReceived SIG
....
Waiting for _stop_event
^CReceived SIG
_stop_event set.
我可以继续发送信号。进程不会退出,输出为:
Waiting for _stop_event
^CReceived SIG
Waiting for _stop_event
^CReceived SIG
^CReceived SIG
^CReceived SIG
^CReceived SIG
....
Waiting for _stop_event
^CReceived SIG
_stop_event set.
如果我将Class1.wait方法替换为event.is_set的检查,则一切正常:
def wait(self):
print("Waiting for _stop_event")
while True:
if Class1._stop_event.is_set():
print("_stop_event set.")
break
进程退出,输出为:
Waiting for _stop_event
^CReceived SIG
Waiting for _stop_event
^CReceived SIG
^CReceived SIG
^CReceived SIG
^CReceived SIG
....
Waiting for _stop_event
^CReceived SIG
_stop_event set.
设置事件后如何使event.wait返回?
wait方法不再超时的原因是什么?信号只在主线程上处理。如果主线程在系统调用中被阻塞,则该系统调用将引发InterruptedError 从Python文档中: 它们[信号]只能出现在Python的“原子”指令之间 翻译 例如,time.sleep会引发中断错误。似乎event.wait方法无法正确处理此场景。它不会引发中断错误,只是开始挂起。这看起来像Python中的一个bug
更新: 我将其缩小为multiprocessing.Event中的死锁。如果主线程正在等待一个事件,同时,一个信号会在中断的主线程上设置该事件,则multiprocessing.event.set()和multiprocessing.event.wait()方法会互相死锁 此外,该行为严重依赖于平台。例如,time.sleep()会在Windows上引发中断错误,但在Linux上只会返回
一个非常笨拙的解决方法是让主线程自由处理信号
import multiprocessing
import signal
import threading
import time
class Class1(object):
_stop_event = multiprocessing.Event()
@staticmethod
def interrupt():
Class1._stop_event.set()
def wait_timeout(self):
print("Waiting for _stop_event")
if Class1._stop_event.wait(30):
print("_stop_event set.")
else:
print("Timeout")
def stop(signum, frame):
print("Received SIG")
Class1.interrupt()
exit_event.set()
def run():
c = Class1()
c.wait_timeout()
t = threading.Thread(target=run)
t.daemon = False
t.start()
exit_event = multiprocessing.Event()
signal.signal(signal.SIGINT, stop)
while not exit_event.is_set():
# Keep a main thread around to handle the signal and
# that thread must not be in a event.wait()!
try:
time.sleep(500)
except InterruptedError:
# We were interrupted by the incoming signal.
# Let the signal handler stop the process gracefully.
pass
这真是他妈的丑。有人请提供一个更优雅的解决方案……你们会喜欢这个的。使用
threading.Event
,而不是多处理.Event
。然后,当您按^C
时,信号处理程序将被调用,就像它应该被调用一样
来源
输出
您也可以使用模块的
pause()
函数代替Event().wait()
<代码>信号。暂停()休眠,直到进程接收到信号。在这种情况下,当接收到SIGINT时,signal.pause()
退出,不返回任何内容。请注意,根据文档,此功能在Windows上不起作用。我在Linux上试过,它对我很有效
我在这篇文章中遇到了这个解决方案。基于Alex出色的调查,这意味着从另一个线程设置标志不会导致死锁。如果中断方法更改如下,则原始代码将起作用:
from threading import Thread
(...)
@staticmethod
def interrupt():
Thread(target=Class1._stop_event.set).start()
美好的在Linux上对我来说很有魅力。但是,没有在Windows上的“模拟”SIGINT上设置事件。对我来说,解决问题的方法是使用
多处理.event
和而不是退出事件。is\u set()
而不是使用退出事件.wait()
。谢谢您还可以将exit\u event.set()
委托给另一个线程,而不是调用它并创建死锁。基本上,信号回调变为:Thread(target=exit\u event.set).start()