Это связано с тем, что обработчик сигнала прерывает нормальный поток выполнения в главном потоке.Любой код, который вы вызываете из обработчика сигнала, должен иметь возможность иметь дело с состоянием программы с произвольной позиции при выполнении программы.
Например, рассмотрим следующее:
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
работал как способ сигнализации потока реактора из другого потока, он должен учитывать возможность этих промежуточных внутренних состояний - и это так.Благодаря этому также безопасно использовать внутри обработчика сигнала.