Python-Skript kann nicht mit Strg-C beendet werden

117

Ich teste Python-Threading mit dem folgenden Skript:

import threading

class FirstThread (threading.Thread):
    def run (self):
        while True:
            print 'first'

class SecondThread (threading.Thread):
    def run (self):
        while True:
            print 'second'

FirstThread().start()
SecondThread().start()

Dies läuft in Python 2.7 unter Kubuntu 11.10. Ctrl+ Cwird es nicht töten. Ich habe auch versucht, einen Handler für Systemsignale hinzuzufügen, aber das hat nicht geholfen:

import signal 
import sys
def signal_handler(signal, frame):
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)

Um den Prozess zu beenden, beende ich ihn durch PID, nachdem ich das Programm mit Ctrl+ in den Hintergrund geschickt habe Z, was nicht ignoriert wird. Warum wird Ctrl+ Cso beharrlich ignoriert? Wie kann ich das beheben?

dotancohen
quelle
@dotancohen funktioniert es unter Windows?
Kiriloff
@vitaibian: Ich habe nicht unter Windows getestet, aber es scheint nicht betriebssystemspezifisch.
Dotancohen

Antworten:

178

Ctrl+ Cbeendet den Hauptthread, aber da sich Ihre Threads nicht im Dämonmodus befinden, werden sie weiter ausgeführt, und das hält den Prozess am Leben. Wir können sie zu Dämonen machen:

f = FirstThread()
f.daemon = True
f.start()
s = SecondThread()
s.daemon = True
s.start()

Aber dann gibt es noch ein anderes Problem: Sobald der Haupt-Thread Ihre Threads gestartet hat, gibt es nichts mehr zu tun. Es wird also beendet und die Threads werden sofort zerstört. Lassen Sie uns also den Haupt-Thread am Leben erhalten:

import time
while True:
    time.sleep(1)

Jetzt wird der Druck "zuerst" und "zweitens" fortgesetzt, bis Sie Ctrl+ drücken C.

Bearbeiten: Wie Kommentatoren hervorgehoben haben, haben die Daemon-Threads möglicherweise keine Chance, Dinge wie temporäre Dateien zu bereinigen. Wenn Sie das brauchen, fangen Sie das KeyboardInterruptam Hauptgewinde und lassen Sie es das Bereinigen und Herunterfahren koordinieren. Aber in vielen Fällen ist es wahrscheinlich gut genug, Daemon-Threads plötzlich sterben zu lassen.

Thomas K.
quelle
6
Sie sollten erwähnen, dass Threads auf diese Weise nicht ordnungsgemäß gestoppt und einige Ressourcen nicht freigegeben werden.
Tommaso Barbugli
1
Nun, Strg-C ist niemals eine anmutige Möglichkeit, etwas zu stoppen. Ich bin nicht sicher, welche Ressourcen übrig bleiben würden - sollte das Betriebssystem beim Beenden des Prozesses nichts zurückfordern?
Thomas K
7
@ThomasK Temporäre Dateien, die von erstellt wurden, tempfile.TemporaryFile()können beispielsweise auf der Festplatte verbleiben .
Feuermurmel
1
@ deed02392: Ich weiß nicht genau, was mit dem Haupt-Thread passiert, aber soweit ich weiß, können Sie nach dem Beenden nichts damit anfangen. Der Vorgang wird beendet, wenn alle Nicht-Daemon-Threads abgeschlossen sind. Eltern-Kind-Beziehungen kommen nicht dazu.
Thomas K
4
Sieht aus wie in python3 Sie weitergeben könnendaemon=True anThread.__init__
Ryan Haining
3

Ich denke, es ist am besten, join () für Ihre Threads aufzurufen, wenn Sie erwarten, dass sie sterben. Ich habe mir mit Ihrem Code etwas Freiheit genommen, um die Schleifen zu beenden (Sie können dort auch alle erforderlichen Bereinigungsanforderungen hinzufügen). Der variable Würfel wird bei jedem Durchgang auf Wahrheit überprüft, und wenn er wahr ist, wird das Programm beendet.

import threading
import time

class MyThread (threading.Thread):
    die = False
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name

    def run (self):
        while not self.die:
            time.sleep(1)
            print (self.name)

    def join(self):
        self.die = True
        super().join()

if __name__ == '__main__':
    f = MyThread('first')
    f.start()
    s = MyThread('second')
    s.start()
    try:
        while True:
            time.sleep(2)
    except KeyboardInterrupt:
        f.join()
        s.join()
Johan Snowgoose
quelle
while Trueist albern, sollten Sie joindirekt - und diese Überschreibungsfunktion ist irgendwie fraglich. Vielleicht , def join(self, force=False): if force: self.die = Trueso dass join()unverändert von join(force=True)kills sie. Aber selbst dann ist es besser, beide Threads zu informieren, bevor Sie sich einem anschließen.
o11c
0

Eine verbesserte Version der Antwort von @Thomas K:

  • Definieren einer Assistentenfunktion is_any_thread_alive()nach diesem Kern , die die main()automatisch beenden kann .

Beispielcodes:

import threading

def job1():
    ...

def job2():
    ...

def is_any_thread_alive(threads):
    return True in [t.is_alive() for t in threads]

if __name__ == "__main__":
    ...
    t1 = threading.Thread(target=job1,daemon=True)
    t2 = threading.Thread(target=job2,daemon=True)
    t1.start()
    t2.start()

    while is_any_thread_alive([t1,t2]):
        time.sleep(0)
Hansimov
quelle