sigwait 的行为不符合预期

问题描述 投票:0回答:1

仅限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()

行为与原始代码相同

python signals
1个回答
0
投票

这并不是一个完整的答案,因为我无法解释为什么 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

但是,程序随后会无限期地阻塞

总而言之,线程中的信号处理似乎是我不完全理解的事情。

如果有人能够对这种行为给出完整的解释,我将永远感激不已

© www.soinside.com 2019 - 2024. All rights reserved.