Python 2.6.x Theading / Signals / Atexit не удастся в некоторых версиях?

StackOverflow https://stackoverflow.com/questions/3713360

Вопрос

Я видел много вопросов, связанных с этим ... но мой код работает на Python 2.6.2 и терпит неудачу работать над Python 2.6.5. Я ошибаюсь, думая, что все функции Atexit, зарегистрированные через этот модуль, не вызываются, когда программа убита сигналом »,« вещь не должна учитываться здесь, потому что я ловлю сигнал, а затем выхожу чисто? Что тут происходит? Какой правильный способ сделать это?

import atexit, sys, signal, time, threading

terminate = False
threads = []

def test_loop():
    while True:
        if terminate:
            print('stopping thread')
            break
        else:
            print('looping')
            time.sleep(1)

@atexit.register
def shutdown():
    global terminate
    print('shutdown detected')
    terminate = True
    for thread in threads:
        thread.join()

def close_handler(signum, frame):
    print('caught signal')
    sys.exit(0)

def run():
    global threads
    thread = threading.Thread(target=test_loop)
    thread.start()
    threads.append(thread)

    while True:
        time.sleep(2)
        print('main')

signal.signal(signal.SIGINT, close_handler)

if __name__ == "__main__":
    run()

Python 2.6.2:

$ python halp.py 
looping
looping
looping
main
looping
main
looping
looping
looping
main
looping
^Ccaught signal
shutdown detected
stopping thread

Python 2.6.5:

$ python halp.py 
looping
looping
looping
main
looping
looping
main
looping
looping
main
^Ccaught signal
looping
looping
looping
looping
...
looping
looping
Killed <- kill -9 process at this point

Основной поток на 2.6.5, по -видимому, никогда не выполняет функции Atexit.

Это было полезно?

Решение

Корневая разница здесь на самом деле не связана как с сигналами, так и с Atexit, а скорее изменением поведения sys.exit.

До 2,6,5, sys.exit (Точнее, SystemExit пойман на верхнем уровне) приведет к выходу интерпретатора; Если бы потоки все еще работали, они были бы прекращены, как и с потоками POSIX.

Около 2.6.5, поведение изменилось: эффект sys.exit теперь по сути, то же самое, что и возврат от основной функции программы. Когда вы это сделаете тот-В обеих версиях-переводчик ждет, пока все потоки будут соединены перед выходом.

Соответствующее изменение заключается в том, что Py_Finalize Теперь звонит wait_for_thread_shutdown() Рядом с вершиной, где это не было раньше.

Это поведенческое изменение кажется неверным, в первую очередь потому, что оно больше не функционирует как задокументированные, а это просто: «Выход из Python». Практический эффект больше не выйдет из Python, а просто для выхода из потока. (В качестве примечания, sys.exit никогда не выходил из Python, когда вы называли из другой ветки, но это неясное расхождение от документированного поведения не оправдывает гораздо большую.)

Я вижу привлекательность нового поведения: вместо двух способов выйти из основного потока («Выйти и ждать потоков» и «сразу выходить»), есть только один, так как sys.exit по существу идентична простому возвращению из верхняя функция. Тем не менее, это нарушающее изменение и расходится от документированного поведения, которое намного перевешивает это.

Из -за этого изменения после sys.exit Из обработчика сигнала выше, интерпретатор сидит вокруг, ожидая выхода потоков, а затем работает atexit Хэндлеры после того, как они это сделают. Поскольку это сам обработчик, который говорит потокам выходить, результатом является тупик.

Другие советы

Выход должное Для сигнала не то же самое, что выход из в пределах обработчик сигнала. Поймать сигнал и выход с помощью sys.exit - это чистый выход, а не выход из -за обработчика сигнала. Итак, да, я согласен, что он должен запускать обработчики Atexit здесь-по крайней мере, в принципе.

Тем не менее, в обработчиках сигналов есть что -то сложное: они совершенно асинхронные. Они могут прервать поток программы в любое время, между любым виртуальным кодом. Возьмите этот код, например. (Рассматривать это как ту же форму, что и ваш код выше; я пропустил код для краткости.)

import threading
lock = threading.Lock()
def test_loop():
    while not terminate:
        print('looping')
        with lock:
             print "Executing synchronized operation"
        time.sleep(1)
    print('stopping thread')

def run():
    while True:
        time.sleep(2)
        with lock:
             print "Executing another synchronized operation"
        print('main')

Здесь есть серьезная проблема: сигнал (например, ^c) может быть получен во время проведения run () lock. Анкет Если это произойдет, ваш обработчик сигнала будет запущен с замком. Затем он будет ждать, пока test_loop выйдет, и если этот поток ждет блокировки, вы будете тупиться.

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

do_shutdown = False
def close_handler(signum, frame):
    global do_shutdown
    do_shutdown = True
    print('caught signal')

def run():
    while not do_shutdown:
        ...

Я предпочитаю, чтобы избежать выхода из программы с помощью SYS.ESIT полностью и явно выполнять очистку в основной точке выхода (например, конец run ()), но вы можете использовать здесь, если хотите.

Я не уверен, было ли это полностью изменилось, но это так, как я сделал Atexit в 2.6.5


atexit.register(goodbye)

def goodbye():
    print "\nStopping..."
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top