Frage

Ich habe eine Menge von Fragen im Zusammenhang mit dieser gesehen ... aber mein Code funktioniert auf Python 2.6.2 und nicht , um die Arbeit an Python 2.6.5. Bin ich falsch im Denken, dass die ganze atexit „Funktionen über dieses Modul registriert werden nicht aufgerufen, wenn das Programm durch ein Signal getötet wird“, was soll hier nicht zählen, weil ich das Signal bin fangen und dann sauber austritt? Was ist denn hier los? Was ist der richtige Weg, dies zu tun?

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

Der Haupt-Thread auf 2.6.5 scheint nie die atexit Funktionen auszuführen.

War es hilfreich?

Lösung

Die Wurzel Unterschied hier ist, tatsächlich in keinem Zusammenhang mit den beiden Signalen und atexit, sondern eine Änderung des Verhaltens von sys.exit.

Vor der Umgebung 2.6.5, sys.exit (genauer gesagt, Systemexit auf der obersten Ebene gefangen) würde die Dolmetscher Ausgang verursachen; wenn Fäden noch liefen, würden sie beendet werden, ebenso wie mit POSIX-Threads.

Um 2.6.5, das Verhalten geändert: Die Wirkung von sys.exit ist nun im Wesentlichen der gleiche wie von der Hauptfunktion des Programms zurück. Wenn Sie das tun , die - in beiden Versionen -. Der Interpreter wartet auf alle Threads vor dem Verlassen verbunden werden

Die entsprechende Änderung ist, dass Py_Finalize jetzt wait_for_thread_shutdown() in der Nähe der Spitze ruft, wo es vorher nicht.

Diese Verhaltensänderung scheint falsch, vor allem, weil es nicht mehr als dokumentiert, das ist einfach: „Beenden von Python“ Der praktische Effekt ist nicht mehr zu verlassen Python, sondern einfach den Thread zu beenden. (Als Randbemerkung, sys.exit hat Python nie verlassen, wenn sie von einem anderen Thread aufgerufen, aber das obskure divergance von dokumentierten Verhalten rechtfertigt nicht einen viel größeren.)

kann ich die Attraktivität des neuen Verhaltens zu sehen: statt zwei Möglichkeiten, um den Haupt-Thread ( „exit und warte auf Threads“ und „exit sofort“) zu verlassen, gibt es nur eine, wie sys.exit einfach im Wesentlichen identisch ist Rückkehr von der Spitze-Funktion. Allerdings ist es eine Bruch Änderung und divergiert von dokumentierten Verhalten, die weit schwerer wiegt als das.

Aufgrund dieser Änderung, nach sys.exit aus den Signal-Handler oben sitzt der Dolmetscher für Threads Ausfahrt warten, um und läuft dann atexit Handler, nachdem sie tun. Da es der Handler ist selbst, die Fäden zu verlassen erzählt, ist das Ergebnis ein Deadlock.

Andere Tipps

Beenden durch , um ein Signal ist nicht das gleiche wie Verlassen von in ein Signal-Handler. ein Signal und Aussteigen mit sys.exit Fangen ist eine saubere Ausfahrt, kein Ausgang aufgrund eines Signal-Handler. Also, ja, ich stimme zu, dass es atexit Handler hier laufen soll -. Zumindest im Prinzip

Allerdings ist es etwas heikel über Signal-Handler: sie vollständig asynchron sind. Sie können den Programmablauf jederzeit zwischen einem beliebigen VM Opcode unterbrechen. Nehmen Sie diesen Code, zum Beispiel. (Treat dies als die gleiche Form wie der Code oben;. I-Code der Kürze halber weggelassen haben)

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')

Es ist ein ernstes Problem hier: ein Signal (zB ^ C.) Empfangen werden kann, während run () lock hält. Wenn das geschieht, werden die Signal-Handler mit dem Schloss noch gehalten ausgeführt werden. Es wird dann für test_loop bis Ausfahrt warten, und wenn das Gewinde für die Sperre wartet, werden Sie Deadlock.

Dies ist eine ganze Kategorie von Problemen, und es ist, warum ein Los von APIs sagen, sie nicht rufen aus Signal-Handler. Stattdessen sollten Sie einen Flag setzen, den Haupt-Thread zu sagen, zu einem geeigneten Zeitpunkt zu beenden.

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

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

Meine Präferenz ist vollständig mit sys.exit Beenden des Programms zu vermeiden und explizit tun Bereinigung an der Hauptausgangspunkt (z. B. das Ende des Laufes ()), aber Sie können atexit hier, wenn Sie wollen.

Ich bin mir nicht sicher, ob dies völlig verändert wurde, aber das ist, wie ich meine atexit in 2.6.5

getan haben

atexit.register(goodbye)

def goodbye():
    print "\nStopping..."
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top