Impossibile uccidere lo script Python con Ctrl-C


117

Sto testando il thread Python con il seguente script:

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

Questo è in esecuzione in Python 2.7 su Kubuntu 11.10. Ctrl+ Cnon lo ucciderà. Ho anche provato ad aggiungere un gestore per i segnali di sistema, ma ciò non ha aiutato:

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

Per terminare il processo, lo sto uccidendo tramite PID dopo aver inviato il programma in background con Ctrl+ Z, che non viene ignorato. Perché Ctrl+ Cviene ignorato in modo così persistente? Come posso risolverlo?


@dotancohen funziona su Windows?
kiriloff

@vitaibian: non ho provato su Windows, ma sembra che non sia specifico del sistema operativo.
dotancohen

Risposte:


178

Ctrl+ Ctermina il thread principale, ma poiché i vostri thread non sono in modalità demone, continuano a funzionare e questo mantiene il processo in vita. Possiamo crearli daemon:

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

Ma poi c'è un altro problema: una volta che il thread principale ha avviato i tuoi thread, non c'è nient'altro da fare. Quindi esce ei thread vengono distrutti all'istante. Quindi manteniamo vivo il thread principale:

import time
while True:
    time.sleep(1)

Ora manterrà la stampa "primo" e "secondo" finché non premi Ctrl+ C.

Modifica: come hanno sottolineato i commentatori, i thread del demone potrebbero non avere la possibilità di ripulire cose come i file temporanei. Se ne hai bisogno, prendi il KeyboardInterruptthread principale e fai in modo che coordini la pulizia e l'arresto. Ma in molti casi, lasciare che i thread del demone muoiano improvvisamente è probabilmente sufficiente.


6
dovresti ricordare che in questo modo i thread non vengono interrotti correttamente e alcune risorse non vengono rilasciate.
Tommaso Barbugli

1
Bene, Ctrl-C non è mai un modo grazioso per fermare qualcosa. Non sono sicuro di quali risorse rimarrebbero - il sistema operativo non dovrebbe reclamare qualcosa quando il processo termina?
Thomas K

7
@ThomasK I file temporanei creati da tempfile.TemporaryFile()possono essere lasciati su disco, ad esempio.
Feuermurmel

1
@ deed02392: Non so esattamente cosa succede al thread principale, ma per quanto ne so non puoi farci niente dopo che è uscito. Il processo terminerà quando tutti i thread non daemon saranno terminati; le relazioni genitore-figlio non entrano in questo.
Thomas K

4
Sembra che in python3 puoi passaredaemon=True aThread.__init__
Ryan Haining


3

Penso che sia meglio chiamare join () sui tuoi thread quando ti aspetti che muoiano. Mi sono preso un po 'di libertà con il tuo codice per far terminare i cicli (puoi aggiungere anche lì qualsiasi necessità di pulizia). La variabile die viene verificata per la verità ad ogni passaggio e quando è True il programma esce.

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

while Trueè sciocco, dovresti joindirettamente - e quella funzione sovrascritta è piuttosto discutibile. Forse è def join(self, force=False): if force: self.die = Truecosì che join()è invariato join(force=True)uccidendoli. Ma anche in questo caso, è meglio informare entrambi i thread prima di unirsi a uno.
o11c

0

Una versione migliorata della risposta di @Thomas K:

  • Definire una funzione assistente in is_any_thread_alive()base a questa sintesi , che può terminare main()automaticamente.

Codici di esempio:

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.