Python - twisted reactor - 从线程角度看 callLater 和 callFromThread 的区别

Python - twisted reactor - difference between callLater and callFromThread from threading perspective

我有一个使用 twisted reactor 的 python class。当它得到 SIGINT 时;它从信号处理函数调用 reactor.callLater(0,sys.exit)

我观察到 callLater(0, sys.exit) 我的进程需要一些时间才能退出,大约 30 秒,如果我将其替换为 reactor.callFromThread(sys.exit) 然后我看到我的进程立即退出。

我无法理解这种行为背后的原因,为什么 callLater 需要时间而 callFromThread 却不是。

这是因为信号处理程序中断了主线程上的正常执行流程。您从信号处理程序调用的任何代码都必须能够处理程序执行过程中任意位置的程序状态。

例如,考虑这个:

class Foo(object):
    def __init__(self):
        self.x = 0
        self.y = 1

    def bar(self):
        x = self.x
        self.x = self.y
        self.y = x

    def sigint(self):
        print(self.x + self.y)

在正常的执行过程中,您绝不会期望 sigint 打印除 1 之外的任何内容。但是,如果 sigint 作为信号处理程序安装,并且在行 self.x = self.yself.y = x 之间传递信号,那么它将看到 self.x 等于 1 和 self.y 等于 1 并打印 2.

因此,您通常只能依赖标记为 "signal safe" 或 "reentrant safe" 的 API。这些 API 的实现方式考虑了信号处理程序的调用方式,并避免因意外的中间内部状态而被绊倒。例如,Foo class 的信号安全版本可能如下所示:

class Foo(object):
    def __init__(self):
        self.x = 0
        self.y = 1
        self._bar_lock = threading.Lock()

    def bar(self):
        with self._bar_lock:
            x = self.x
            self.x = self.y
            self.y = x

    def sigint(self):
        with self._bar_lock:
            print(self.x + self.y)

Twisted 的 callFromThread 是信号安全的,其本质上与线程安全的原因相同。 API 可以在任何时候从非主线程调用,并遇到相同的潜在不一致的中间内部状态。对于 callFromThread 作为从另一个线程向反应器线程发出信号的方式,它必须考虑这些中间内部状态的可能性 - 它确实这样做了。因此,在信号处理程序中使用也是安全的。