Python - скрученный реактор - разница между callLater и callFromThread с точки зрения потоков - PullRequest
0 голосов
/ 26 февраля 2019

У меня есть класс Python, который использует twisted reactor.Когда он становится SIGINT;он вызывает reactor.callLater(0,sys.exit) из функции обработчика сигнала.

То, что я заметил, было с callLater(0, sys.exit) моему процессу требуется некоторое время для завершения, около 30 секунд, если я заменил его на reactor.callFromThread(sys.exit), то я вижу, что мой процессвыход немедленно.

Я не могу понять причину этого поведения, почему callLater требует времени, а с callFromThread его нет.

1 Ответ

0 голосов
/ 26 февраля 2019

Это связано с тем, что обработчик сигнала прерывает нормальный поток выполнения в главном потоке.Любой код, который вы вызываете из обработчика сигнала, должен иметь возможность иметь дело с состоянием программы с произвольной позиции при выполнении программы.

Например, рассмотрим следующее:

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)

Inпри нормальном ходе выполнения вы никогда бы не ожидали, что 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)

Twisted callFromThread является сигнально-безопасным по той же причине, что и потокобезопасный.API может быть вызван из неосновного потока по существу в любой точке и сталкиваться с такими же потенциально несовместимыми промежуточными внутренними состояниями.Чтобы callFromThread работал как способ сигнализации потока реактора из другого потока, он должен учитывать возможность этих промежуточных внутренних состояний - и это так.Благодаря этому также безопасно использовать внутри обработчика сигнала.

...