Почему использование threading.Event приводит к тому, что SIGTERM не перехватывается? - PullRequest
16 голосов
/ 23 июня 2010

У меня есть многопоточный демон Python.Как и любой хороший демон, он хочет запустить все свои рабочие потоки, а затем подождать, пока ему не скажут завершить работу.Обычный сигнал для завершения - SIGTERM, и в большинстве языков я бы предпочел завершить, ожидая события или мьютекса, поэтому использование threading.Event имело смысл для меня.Проблема в том, что Event объект Python и сигналы Unix, похоже, не очень хорошо играют вместе.

Это работает, как и ожидалось, и заканчивается SIGTERM:

import signal
import time

RUN = True

def handle(a, b):
    global RUN
    print "handled"
    RUN = False

signal.signal(signal.SIGTERM, handle)
while RUN:
    time.sleep(0.250)
print "Stopping"

, но этов результате SIGTERM не доставляется (т. е. совершенно отдельно от выхода, «обработанный» никогда не печатается):

import signal
import threading

RUN_EVENT = threading.Event()

def handle(a, b):
    print "handled"
    RUN_EVENT.set()

signal.signal(signal.SIGTERM, handle)
RUN_EVENT.wait()
print "Stopping"

Итак, мой вопрос:

  1. Я неправильно используюthreading.Event каким-то образом?
  2. Если я не являюсь, есть ли альтернатива, кроме механизма опроса и сна, из первого примера?
  3. Кроме того, если нет, почемус помощью threading.Event убить обработчик сигнала?

Ответы [ 2 ]

15 голосов
/ 23 июня 2010

Из Документация Python по сигналам :

Хотя обработчики сигналов Python вызываются асинхронно, поскольку это касается пользователя Python, они могут происходить только между «атомарными» инструкциямиинтерпретатора Python.Это означает, что сигналы, поступающие во время длинных вычислений, реализованных исключительно на языке C (например, совпадения с регулярными выражениями в больших объемах текста), могут задерживаться на произвольное количество времени.

Я тестировал различные классы threading и thread, и ни один из них не работал так, как вы хотите - возможно, это из-за того, как Python обрабатывает сигналы.

In signal, однако есть функция pause(), которая спит до тех пор, пока процесс не получит сигнал.Ваш модифицированный пример будет выглядеть так:

import signal

RUN = True

def handle(a, b):
    global RUN
    print "handled"
    RUN = False

signal.signal(signal.SIGTERM, handle)
while RUN:
    signal.pause()
print "Stopping"

Я проверил его в Linux, он работает.Я не думаю, что это больше классифицируется как «опрос и сон», если ваше приложение не использует много других сигналов.

0 голосов
/ 26 июня 2019

В Python 3 это работает:

from threading import Event
from os import getpid
from signal import SIGTERM, SIGINT, signal

stop_event = Event()

def handler(signum, frame):
    stop_event.set()

def main():
    signal(SIGTERM, handler)
    signal(SIGINT, handler)
    print('start, pid:', getpid())
    stop_event.wait()
    print('done')

if __name__ == '__main__':
    main()

Похоже, что в Python 2.7 это работает, только если указать интервал ожидания: stop_event.wait(number_of_seconds)

...