Python-扭曲的反应器-从线程角度看callLater和callFromThread之间的区别

时间:2019-02-26 09:50:08

标签: python twisted

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

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

我无法理解这种行为背后的原因,为什么callLater花时间而没有callFromThread

1 个答案:

答案 0 :(得分:1)

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

例如,考虑一下:

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和{{1} }等于1并显示self.y

因此,您通常只能依赖标记为“信号安全”或“可重入安全”的API。这些API的实现方式应考虑到调用信号处理程序的方式,并避免在令人惊讶的中间内部状态上跳闸。例如,2类的信号安全版本可能看起来像这样:

Foo

Twisted的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) 是信号安全的,其本质与线程安全的原因相同。可以在任何时候从非主线程调用该API,并且会遇到相同的可能不一致的中间内部状态。为了使callFromThread能够从另一个线程向反应堆线程发出信号,它必须考虑这些中间内部状态的可能性-并且这样做。因此,在信号处理程序内部使用也是安全的。