仅限Unix
from threading import Thread
from signal import signal, alarm, sigwait, SIGALRM
class Check(Thread):
def __init__(self):
super().__init__()
signal(SIGALRM, Check.handler)
@staticmethod
def handler(*_):
print("Hello")
def run(self):
for _ in range(5):
alarm(1)
print("Waiting...")
sigwait((SIGALRM,))
print("done")
if __name__ == "__main__":
(check := Check()).start()
check.join()
预期行为:
我预计以下输出会重复 5 次:
Waiting...
Hello
done
但是,“done”永远不会被打印,因为运行时在 sigwait()
上“阻塞”打印出“Hello”。因此我知道信号处理程序已被调用。
如果 SIGALARM 已发出信号(通过 alarm())并得到处理,为什么 sigwait() 不返回?
平台:
macOS 14.2.1
Python 3.12.1
编辑以包含对 pthread_sigmask 的调用
from threading import Thread
from signal import signal, alarm, sigwait, pthread_sigmask, SIGALRM, SIG_BLOCK
class Check(Thread):
def __init__(self):
super().__init__()
signal(SIGALRM, self.handler)
def handler(self, *_):
print("Hello")
def run(self):
mask = SIGALRM,
pthread_sigmask(SIG_BLOCK, mask)
for _ in range(5):
alarm(1)
print("Waiting...")
sigwait(mask)
print("done")
if __name__ == "__main__":
(check := Check()).start()
check.join()
行为与原始代码相同
这并不是一个完整的答案,因为我无法解释为什么 sigwait 在线程上下文中的行为不符合我的预期。然而,这是我凭经验确定的。
考虑这个主程序:
from signal import sigwait, SIGALRM, alarm
from time import time
if __name__ == "__main__":
a = time()
alarm(1)
sigwait((SIGALRM,))
b = time()
print(round(b-a, 2))
其输出为:
1.01
这就是我所期望的 - 即大约 1 秒的暂停
现在考虑一下:
from threading import Thread
from signal import sigwait, SIGALRM, alarm
from time import time
class SigTest(Thread):
def __init__(self):
super().__init__()
def run(self):
a = time()
alarm(1)
sigwait((SIGALRM,))
b = time()
print(round(b-a, 2))
if __name__ == "__main__":
(t := SigTest()).start()
t.join()
当我运行它时,python 可执行文件终止,因为警报既没有被捕获,也没有以任何其他方式观察到。所以在我的系统上的输出是:
zsh: alarm /usr/local/bin/python3 /Users/CtrlZ/Python/Jan08.py
如果我通过添加来增强此代码:
pthread_sigmask(SIG_BLOCK, (SIGALRM,))
...对于 run() 函数重写,行为是相同的 - 即,python 由于未观察到 SIGALRM 而终止。
现在让我们进一步介绍一个信号处理程序:
from threading import Thread
from signal import signal, sigwait, SIGALRM, SIG_BLOCK, alarm, pthread_sigmask
from time import time
class SigTest(Thread):
def __init__(self):
super().__init__()
signal(SIGALRM, SigTest.handler)
@staticmethod
def handler(signum, _):
print(f"Signal {signum} handled")
def run(self):
mask = SIGALRM,
# note that the behaviour does not change...
# ...if pthread_sigmask is called in this way
#pthread_sigmask(SIG_BLOCK, mask)
a = time()
alarm(1)
sigwait(mask)
b = time()
print(round(b-a, 2))
if __name__ == "__main__":
(t := SigTest()).start()
t.join()
其输出为:
Signal 14 handled
但是,程序随后会无限期地阻塞
总而言之,线程中的信号处理似乎是我不完全理解的事情。如果有人能够对这种行为给出完整的解释,我将永远感激不已