首页 > 解决方案 > Python - 扭曲的反应器 - 从线程的角度来看 callLater 和 callFromThread 之间的区别

问题描述

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

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

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

标签: pythontwisted

解决方案


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

例如,考虑一下:

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.y那么self.y = x它将看到self.x等于 1 和self.y等于 1 并打印2

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

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)

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


推荐阅读