C'è un modo per uccidere un thread?


Risposte:


672

In genere è un cattivo modello uccidere un thread bruscamente, in Python e in qualsiasi lingua. Pensa ai seguenti casi:

  • il thread contiene una risorsa critica che deve essere chiusa correttamente
  • il thread ha creato anche molti altri thread che devono essere uccisi.

Il modo migliore di gestirlo se te lo puoi permettere (se gestisci i tuoi thread) è avere un flag exit_request che ogni thread controlla a intervalli regolari per vedere se è il momento di uscire.

Per esempio:

import threading

class StoppableThread(threading.Thread):
    """Thread class with a stop() method. The thread itself has to check
    regularly for the stopped() condition."""

    def __init__(self,  *args, **kwargs):
        super(StoppableThread, self).__init__(*args, **kwargs)
        self._stop_event = threading.Event()

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()

In questo codice, dovresti chiamare stop()il thread quando vuoi che esca e aspettare che il thread esca correttamente usando join(). Il thread dovrebbe controllare il flag di stop a intervalli regolari.

In alcuni casi, tuttavia, è necessario eliminare un thread. Un esempio è quando si esegue il wrapping di una libreria esterna occupata per chiamate lunghe e si desidera interromperla.

Il codice seguente consente (con alcune restrizioni) di generare un'eccezione in un thread Python:

def _async_raise(tid, exctype):
    '''Raises an exception in the threads with id tid'''
    if not inspect.isclass(exctype):
        raise TypeError("Only types can be raised (not instances)")
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
                                                     ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # "if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"
        ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

class ThreadWithExc(threading.Thread):
    '''A thread class that supports raising exception in the thread from
       another thread.
    '''
    def _get_my_tid(self):
        """determines this (self's) thread id

        CAREFUL : this function is executed in the context of the caller
        thread, to get the identity of the thread represented by this
        instance.
        """
        if not self.isAlive():
            raise threading.ThreadError("the thread is not active")

        # do we have it cached?
        if hasattr(self, "_thread_id"):
            return self._thread_id

        # no, look for it in the _active dict
        for tid, tobj in threading._active.items():
            if tobj is self:
                self._thread_id = tid
                return tid

        # TODO: in python 2.6, there's a simpler way to do : self.ident

        raise AssertionError("could not determine the thread's id")

    def raiseExc(self, exctype):
        """Raises the given exception type in the context of this thread.

        If the thread is busy in a system call (time.sleep(),
        socket.accept(), ...), the exception is simply ignored.

        If you are sure that your exception should terminate the thread,
        one way to ensure that it works is:

            t = ThreadWithExc( ... )
            ...
            t.raiseExc( SomeException )
            while t.isAlive():
                time.sleep( 0.1 )
                t.raiseExc( SomeException )

        If the exception is to be caught by the thread, you need a way to
        check that your thread has caught it.

        CAREFUL : this function is executed in the context of the
        caller thread, to raise an excpetion in the context of the
        thread represented by this instance.
        """
        _async_raise( self._get_my_tid(), exctype )

(Basato su thread Killable di Tomer Filiba. La citazione sul valore restituito di PyThreadState_SetAsyncExcsembra provenire da una vecchia versione di Python .)

Come notato nella documentazione, questo non è un proiettile magico perché se il thread è occupato fuori dall'interprete Python, non intercetterà l'interruzione.

Un buon modello di utilizzo di questo codice è fare in modo che il thread rilevi un'eccezione specifica ed esegua la pulizia. In questo modo, è possibile interrompere un'attività e avere comunque una corretta pulizia.


78
@ Bluebird75: Inoltre, non sono sicuro di avere l'argomento che i thread non dovrebbero essere uccisi bruscamente "perché il thread potrebbe contenere una risorsa critica che deve essere chiusa correttamente": questo è vero anche da un programma principale e dai programmi principali può essere interrotto bruscamente dall'utente (Ctrl-C in Unix, per esempio), nel qual caso cercano di gestire questa possibilità nel modo più piacevole possibile. Quindi, non riesco a vedere ciò che è speciale con i thread e perché non dovrebbero ricevere lo stesso trattamento dei programmi principali (vale a dire che possono essere uccisi improvvisamente). :) Potresti approfondire questo?
Eric O Lebigot,

18
@EOL: D'altra parte, se tutte le risorse che il thread possiede sono risorse locali (file aperti, socket), Linux è ragionevolmente bravo nella pulizia del processo e questo non perde. Ho avuto casi in cui ho creato un server usando socket, e se faccio una brutale interruzione con Ctrl-C, non posso più avviare il programma perché non può vincolare il socket. Devo aspettare 5 minuti. La soluzione corretta era catturare Ctrl-C ed eseguire una deconnessione pulita del socket.
Philippe F,

10
@ Bluebird75: btw. è possibile utilizzare l' SO_REUSEADDRopzione socket per evitare Address already in useerrori.
Messa

12
Nota su questa risposta: almeno per me (py2.6), ho dovuto passare Noneinvece che 0per il res != 1caso, e ho dovuto chiamare ctypes.c_long(tid)e passarlo direttamente a qualsiasi funzione di tipo CTS anziché a quella corrente.
Walt W,

21
Vale la pena ricordare che _stop è già occupato nella libreria di threading di Python 3. Come tale, forse utilizzare una variabile diversa altrimenti si otterrà un errore.
diethreetimes

113

Non esiste un'API ufficiale per farlo, no.

È necessario utilizzare l'API della piattaforma per terminare il thread, ad esempio pthread_kill o TerminateThread. È possibile accedere a tale API, ad esempio tramite pythonwin o tramite ctypes.

Si noti che questo è intrinsecamente pericoloso. Probabilmente porterà a rifiuti non collezionabili (dalle variabili locali dei frame dello stack che diventano rifiuti) e potrebbe portare a deadlock, se il thread che viene ucciso ha il GIL nel punto in cui viene ucciso.


26
Essa sarà portare a situazioni di stallo se il thread in questione detiene il GIL.
Matthias Urlichs,

96

Una multiprocessing.Processlattinap.terminate()

Nei casi in cui voglio uccidere un thread, ma non voglio usare flag / lock / segnali / semafori / eventi / qualunque cosa, promuovo i thread a processi completi. Per il codice che utilizza solo pochi thread, l'overhead non è poi così male.

Ad esempio, è utile per terminare facilmente i "thread" di supporto che eseguono I / O di blocco

La conversione è banale: nel codice correlato sostituisci tutto threading.Threadcon multiprocessing.Processe tutti queue.Queuecon multiprocessing.Queuee aggiungi le chiamate richieste p.terminate()al tuo processo genitore che vuole uccidere suo figliop

Vedere la documentazione di Python permultiprocessing .


Grazie. Ho sostituito coda.Queue con multiprocessing.JoinableQueue e ho seguito questa risposta: stackoverflow.com/a/11984760/911207
David Braun

Molte pagine su questo problema. Questa sembra la soluzione ovvia per molti, credo
geoteca il

6
multiprocessingè carino, ma tieni presente che gli argomenti vengono messi in discussione nel nuovo processo. Quindi, se uno degli argomenti è qualcosa di non selezionabile (come un logging.log), potrebbe non essere una buona idea usare multiprocessing.
Lyager

1
multiprocessinggli argomenti vengono decapitati al nuovo processo su Windows, ma Linux usa il fork per copiarli (Python 3.7, incerto su quali altre versioni). Quindi finirai con il codice che funziona su Linux ma genera errori di pickle su Windows.
nyanpasu64,

multiprocessingcon la registrazione è un affare complicato. Devi usare QueueHandler(vedi questo tutorial ). L'ho imparato a mie spese.
Fanchen Bao,

74

Se stai cercando di terminare l'intero programma, puoi impostare il thread come "demone". vedi Thread.daemon


Questo non ha alcun senso. La documentazione afferma chiaramente che "deve essere impostato prima di chiamare start (), altrimenti viene generato RuntimeError". Quindi, se voglio uccidere un thread che non era originariamente un demone, come posso usarlo?
Raffi Khatchadourian,

27
Raffi Penso che stia suggerendo che lo avresti impostato in anticipo, sapendo che quando il tuo thread principale esce, vuoi che anche i thread del demone escano.
fantabolous

1
Non sono sicuro del perché questa non sia la risposta accettata
eric,

L'impostazione di un thread come demone non è qualcosa che faresti nel caso in cui desideri che il thread continui a funzionare anche se il tuo programma principale viene chiuso?
Michele Piccolini,

Signore, siete il mio eroe del giorno. Esattamente quello che stavo cercando e nessuna confusione da aggiungere.
Blizz l'

42

Come altri hanno già detto, la norma è quella di impostare un flag di stop. Per qualcosa di leggero (nessuna sottoclasse di Thread, nessuna variabile globale), un callback lambda è un'opzione. (Nota tra parentesi if stop().)

import threading
import time

def do_work(id, stop):
    print("I am thread", id)
    while True:
        print("I am thread {} doing something".format(id))
        if stop():
            print("  Exiting loop.")
            break
    print("Thread {}, signing off".format(id))


def main():
    stop_threads = False
    workers = []
    for id in range(0,3):
        tmp = threading.Thread(target=do_work, args=(id, lambda: stop_threads))
        workers.append(tmp)
        tmp.start()
    time.sleep(3)
    print('main: done sleeping; time to stop the threads.')
    stop_threads = True
    for worker in workers:
        worker.join()
    print('Finis.')

if __name__ == '__main__':
    main()

La sostituzione print()con una pr()funzione che scarica sempre ( sys.stdout.flush()) può migliorare la precisione dell'output della shell.

(Testato solo su Windows / Eclipse / Python3.3)


1
Verificato su Linux / Python 2.7, funziona come un fascino. Questa dovrebbe essere la risposta ufficiale, è molto più semplice.
Paul Kenjora,

1
Verificato su Linux Ubuntu Server 17.10 / Python 3.6.3 e funziona.
Marcos,

1
Verificato anche in 2.7. Che bella risposta!
silgon,

Cos'è la pr()funzione?
Alper

35

Questo è basato su thread2 - thread killable (ricetta Python)

Devi chiamare PyThreadState_SetasyncExc (), che è disponibile solo attraverso i tipi.

Questo è stato testato solo su Python 2.7.3, ma è probabile che funzioni con altre versioni 2.x recenti.

import ctypes

def terminate_thread(thread):
    """Terminates a python thread from another thread.

    :param thread: a threading.Thread instance
    """
    if not thread.isAlive():
        return

    exc = ctypes.py_object(SystemExit)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
        ctypes.c_long(thread.ident), exc)
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

Sto usando qualcosa del genere per dare ai miei thread in KeyboardInterruptmodo che abbiano la possibilità di ripulire. Se sono ancora bloccati dopo, allora SystemExitè appropriato o semplicemente uccide il processo da un terminale.
drevicko,

Funziona se il thread è attualmente in esecuzione. Non funziona se il thread si trova in una syscall; l'eccezione verrà silenziosamente ignorata.
Matthias Urlichs,

@MatthiasUrlich ha qualche idea su come rilevare quale sia lo stato di esecuzione del thread, per poter stampare un avviso o riprovare?
Johan Dahlin,

1
@JohanDahlin Potresti aspettare un po '(che, se vuoi riprovare, devi comunque fare) e poi fare il test isAlive (). In ogni caso, mentre questo funzionerebbe, non garantirei che non lascino in giro riferimenti penzolanti. Anche se in teoria è possibile rendere sicuro l'uccisione di thread in CPython, con un uso giudizioso pthread_cleanup_push()/_pop(), sarebbe molto difficile implementarlo correttamente e rallenterebbe notevolmente l'interprete.
Matthias Urlichs,

32

Non dovresti mai uccidere forzatamente un thread senza collaborare con esso.

L'uccisione di un thread rimuove qualsiasi garanzia che provi / infine blocchi impostati in modo da poter lasciare i blocchi bloccati, i file aperti, ecc.

L'unica volta in cui puoi sostenere che uccidere forzatamente i thread è una buona idea è quello di uccidere un programma velocemente, ma mai singoli thread.


11
Perché è così difficile raccontare una discussione, per favore ucciditi quando finisci il tuo ciclo attuale ... Non capisco.
Mehdi

2
Non esiste alcun meccanismo incorporato nella CPU per identificare un "loop" in quanto tale, il meglio che si possa sperare è di usare un qualche tipo di segnale che il codice che è attualmente all'interno del loop controllerà una volta uscito. Il modo corretto di gestire la sincronizzazione dei thread è in modo cooperativo, la sospensione, la ripresa e l'uccisione dei thread sono funzioni destinate ai debugger e al sistema operativo, non al codice dell'applicazione.
Lasse V. Karlsen,

2
@Mehdi: se scrivo (personalmente) il codice nel thread, sì, sono d'accordo con te. Ma ci sono casi in cui sto eseguendo librerie di terzi e non ho accesso al ciclo di esecuzione di quel codice. Questo è un caso d'uso per la funzione richiesta.
Dan H,

@DanH È ancora peggio con il codice di terze parti poiché non hai idea di quale danno possa causare. Se la tua libreria di terze parti non è abbastanza robusta da richiedere l'uccisione, allora dovresti fare una di queste: (1) chiedi all'autore di risolvere il problema, (2) usa qualcos'altro. Se non hai davvero scelta, inserire quel codice in un processo distinto dovrebbe essere più sicuro in quanto alcune risorse sono condivise solo all'interno di un singolo processo.
Phil1970,

25

In Python, semplicemente non puoi uccidere direttamente un thread.

Se NON hai davvero bisogno di avere un Thread (!), Quello che puoi fare, invece di usare il pacchetto threading , è usare il pacchetto multiprocessore . Qui, per terminare un processo, puoi semplicemente chiamare il metodo:

yourProcess.terminate()  # kill the process!

Python ucciderà il tuo processo (su Unix attraverso il segnale SIGTERM, mentre su Windows attraverso la TerminateProcess()chiamata). Fai attenzione ad usarlo mentre usi una coda o una pipe! (potrebbe corrompere i dati nella coda / pipe)

Si noti che multiprocessing.Evente e multiprocessing.Semaphorefunzionano esattamente allo stesso modo di threading.Evente threading.Semaphorerispettivamente. In effetti, i primi sono cloni di questi ultimi.

Se hai davvero bisogno di usare un thread, non c'è modo di ucciderlo direttamente. Quello che puoi fare, tuttavia, è usare un "thread demone" . In effetti, in Python, un thread può essere contrassegnato come demone :

yourThread.daemon = True  # set the Thread as a "daemon thread"

Il programma principale verrà chiuso quando non rimangono più thread non daemon attivi. In altre parole, quando il tuo thread principale (che è, ovviamente, un thread non daemon) finirà le sue operazioni, il programma uscirà anche se ci sono ancora alcuni thread daemon funzionanti.

Nota che è necessario impostare un thread come daemonprima start()che venga chiamato il metodo!

Ovviamente puoi, e dovresti, usarlo daemonanche con multiprocessing. Qui, quando termina il processo principale, tenta di terminare tutti i suoi processi figlio demoniaci.

Infine, si prega, si noti che sys.exit()e os.kill()non sono scelte.


14

Puoi uccidere un thread installando la traccia nel thread che uscirà dal thread. Vedi link allegato per una possibile implementazione.

Uccidi un thread in Python


L'ho già visto Questa soluzione si basa sul controllo autonomo della bandiera
Sudden Def,

1
Una delle poche risposte qui che FUNZIONA effettivamente
Ponkadoodle

5
Due problemi con questa soluzione: (a) l'installazione di un tracciante con sys.settrace () renderà il tuo thread più lento. Fino a 10 volte più lento se è associato al calcolo. (b) non influirà sul thread mentre è in una chiamata di sistema.
Matthias Urlichs,

10

Se si sta chiamando esplicitamente time.sleep()come parte del filo (diciamo polling qualche servizio esterno), con un miglioramento dal metodo di Phillipe è quello di utilizzare il timeout nel event's wait()metodo ovunque tusleep()

Per esempio:

import threading

class KillableThread(threading.Thread):
    def __init__(self, sleep_interval=1):
        super().__init__()
        self._kill = threading.Event()
        self._interval = sleep_interval

    def run(self):
        while True:
            print("Do Something")

            # If no kill signal is set, sleep for the interval,
            # If kill signal comes in while sleeping, immediately
            #  wake up and handle
            is_killed = self._kill.wait(self._interval)
            if is_killed:
                break

        print("Killing Thread")

    def kill(self):
        self._kill.set()

Quindi eseguirlo

t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread

Il vantaggio di utilizzare wait()invece di sleep()ing e controllare regolarmente l'evento è che è possibile programmare in intervalli di sospensione più lunghi, il thread viene interrotto quasi immediatamente (quando si sarebbe altrimenti sleep()ing) e, a mio avviso, il codice per la gestione dell'uscita è significativamente più semplice .


3
perché questo post è stato sottoposto a downgrade? Cosa c'è di sbagliato in questo post? Sembra esattamente quello di cui ho bisogno ....
JDOaktown,

9

È meglio se non uccidi un thread. Un modo potrebbe essere quello di introdurre un blocco "try" nel ciclo del thread e generare un'eccezione quando si desidera interrompere il thread (ad esempio un'interruzione / ritorno / ... che interrompe il tuo per / while / ...). L'ho usato sulla mia app e funziona ...


8

È sicuramente possibile implementare un Thread.stopmetodo come mostrato nel seguente codice di esempio:

import sys
import threading
import time


class StopThread(StopIteration):
    pass

threading.SystemExit = SystemExit, StopThread


class Thread2(threading.Thread):

    def stop(self):
        self.__stop = True

    def _bootstrap(self):
        if threading._trace_hook is not None:
            raise ValueError('Cannot run thread with tracing!')
        self.__stop = False
        sys.settrace(self.__trace)
        super()._bootstrap()

    def __trace(self, frame, event, arg):
        if self.__stop:
            raise StopThread()
        return self.__trace


class Thread3(threading.Thread):

    def _bootstrap(self, stop_thread=False):
        def stop():
            nonlocal stop_thread
            stop_thread = True
        self.stop = stop

        def tracer(*_):
            if stop_thread:
                raise StopThread()
            return tracer
        sys.settrace(tracer)
        super()._bootstrap()

###############################################################################


def main():
    test1 = Thread2(target=printer)
    test1.start()
    time.sleep(1)
    test1.stop()
    test1.join()
    test2 = Thread2(target=speed_test)
    test2.start()
    time.sleep(1)
    test2.stop()
    test2.join()
    test3 = Thread3(target=speed_test)
    test3.start()
    time.sleep(1)
    test3.stop()
    test3.join()


def printer():
    while True:
        print(time.time() % 1)
        time.sleep(0.1)


def speed_test(count=0):
    try:
        while True:
            count += 1
    except StopThread:
        print('Count =', count)

if __name__ == '__main__':
    main()

La Thread3classe sembra eseguire il codice circa il 33% più velocemente della Thread2classe.


3
Questo è un modo intelligente per iniettare controlli per self.__stopessere impostato nel thread. Si noti che come la maggior parte delle altre soluzioni qui, in realtà non interromperà una chiamata di blocco, poiché la funzione di traccia viene chiamata solo quando viene inserito un nuovo ambito locale. Vale anche la pena notare che è sys.settraceveramente pensato per l'implementazione di debugger, profili, ecc. E come tale è considerato un dettaglio di implementazione di CPython e non è garantito che esista in altre implementazioni di Python.
dano,

3
@dano: uno dei maggiori problemi con la Thread2classe è che esegue il codice circa dieci volte più lentamente. Alcune persone potrebbero ancora trovarlo accettabile.
Noctis Skytower,

+1 su questo rallenta considerevolmente l'esecuzione del codice. Suggerirei che l'autore di questa soluzione includa queste informazioni nella risposta.
Vishal,

6
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

t è il tuo Threadoggetto.

Leggi la fonte di Python ( Modules/threadmodule.ce Python/thread_pthread.h) puoi vedere che Thread.identè un pthread_ttipo, quindi puoi fare qualsiasi cosa pthreadpossa fare nell'uso di Python libpthread.


E come si fa su Windows?
iChux,

12
Tu no; non su Windows e nemmeno su Linux. Motivo: il thread in questione potrebbe contenere GIL mentre lo stai facendo (Python rilascia GIL quando chiami in C). In tal caso, il programma si bloccherà immediatamente. Anche se non lo fa, finalmente: i blocchi non verranno eseguiti ecc., Quindi questa è un'idea molto pericolosa.
Matthias Urlichs,

6

La seguente soluzione alternativa può essere utilizzata per uccidere un thread:

kill_threads = False

def doSomething():
    global kill_threads
    while True:
        if kill_threads:
            thread.exit()
        ......
        ......

thread.start_new_thread(doSomething, ())

Questo può essere usato anche per terminare i thread, il cui codice è scritto in un altro modulo, dal thread principale. Possiamo dichiarare una variabile globale in quel modulo e usarla per terminare i thread generati in quel modulo.

Di solito lo uso per terminare tutti i thread all'uscita dal programma. Questo potrebbe non essere il modo perfetto per terminare i thread ma potrebbe aiutare.


Pollice su. Semplice da capire
alyssaeliyah,

6

Sono in ritardo a questo gioco, ma ho lottato con una domanda simile e quanto segue sembra sia risolvere perfettamente il problema per me E mi permette di fare un controllo dello stato del thread di base e di ripulirlo quando il sottoprocesso demone esce:

import threading
import time
import atexit

def do_work():

  i = 0
  @atexit.register
  def goodbye():
    print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
           (i, threading.currentThread().ident))

  while True:
    print i
    i += 1
    time.sleep(1)

t = threading.Thread(target=do_work)
t.daemon = True
t.start()

def after_timeout():
  print "KILL MAIN THREAD: %s" % threading.currentThread().ident
  raise SystemExit

threading.Timer(2, after_timeout).start()

I rendimenti:

0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]

Questa è una risposta eccellente che dovrebbe essere in
cima

Perché rilanciare SystemExitsul after_timeoutthread farebbe qualcosa al thread principale (che sta semplicemente aspettando che il primo esca in questo esempio)?
Davis Herring il

@DavisHerring Non sono sicuro di cosa stai arrivando. SystemExit uccide il thread principale, perché pensi che non farebbe nulla sul thread principale? Senza quella chiamata, il programma continuerà ad attendere sul thread figlio. Puoi anche ctrl + c o usare qualsiasi altro mezzo per uccidere il thread principale, ma questo è un esempio.
slumtrimpet

@slumtrimpet: SystemExitha solo due proprietà speciali: non produce un traceback (quando un thread esce lanciandone uno) e se il thread principale esce lanciandone uno imposta lo stato di uscita (mentre attende comunque altri thread non daemon uscire).
Davis Herring

@DavisHerring Oh, ho capito. Totalmente d'accordo con te. Stavo travisando ciò che abbiamo fatto qui e fraintendendo il tuo commento.
slumtrimpet

4

Una cosa che voglio aggiungere è che se leggi la documentazione ufficiale nel threading lib Python , è consigliabile evitare l'uso di thread "demoniaci", quando non vuoi che i thread finiscano bruscamente, con la bandiera di cui parla Paolo Rovelli .

Dalla documentazione ufficiale:

I thread daemon vengono improvvisamente arrestati allo spegnimento. Le loro risorse (come file aperti, transazioni di database, ecc.) Potrebbero non essere rilasciate correttamente. Se vuoi che i tuoi thread si fermino con grazia, rendili non demonici e usa un meccanismo di segnalazione adatto come un Evento.

Penso che la creazione di thread daemonici dipenda dalla tua applicazione, ma in generale (e secondo me) è meglio evitare di ucciderli o renderli demoniaci. Nel multiprocessing puoi usareis_alive() per verificare lo stato del processo e "terminare" per completarli (Inoltre si evitano problemi GIL). Ma puoi trovare più problemi, a volte, quando esegui il tuo codice in Windows.

E ricorda sempre che se hai "thread attivi", l'interprete Python sarà in esecuzione per aspettarli. (A causa di questo demone può aiutarti se la materia non finisce improvvisamente).


4
Non capisco l'ultimo paragrafo.
Tshepang,

@Tshepang Significa che se nell'applicazione sono presenti thread non daemon in esecuzione, l'interprete Python continuerà a funzionare fino a quando tutti i thread non daemon sono stati eseguiti . Se non ti interessa se i thread terminano quando termina il programma, allora renderli demoni può essere utile.
Tom Myddeltyn,

3

C'è una biblioteca costruita per questo scopo, basta . Sebbene siano ancora applicabili alcune delle stesse precauzioni elencate nel presente documento, almeno questa libreria presenta una tecnica regolare e ripetibile per raggiungere l'obiettivo dichiarato.


1

Mentre è piuttosto vecchio, questa potrebbe essere una soluzione utile per alcuni:

Un piccolo modulo che estende la funzionalità del modulo del threading: consente a un thread di generare eccezioni nel contesto di un altro thread. Alzando SystemExit, puoi finalmente uccidere i thread di Python.

import threading
import ctypes     

def _async_raise(tid, excobj):
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        # """if it returns a number greater than one, you're in trouble, 
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")

class Thread(threading.Thread):
    def raise_exc(self, excobj):
        assert self.isAlive(), "thread must be started"
        for tid, tobj in threading._active.items():
            if tobj is self:
                _async_raise(tid, excobj)
                return

        # the thread was alive when we entered the loop, but was not found 
        # in the dict, hence it must have been already terminated. should we raise
        # an exception here? silently ignore?

    def terminate(self):
        # must raise the SystemExit type, instead of a SystemExit() instance
        # due to a bug in PyThreadState_SetAsyncExc
        self.raise_exc(SystemExit)

Pertanto, consente a un "thread di generare eccezioni nel contesto di un altro thread" e in questo modo, il thread terminato può gestire la terminazione senza controllare regolarmente un flag di interruzione.

Tuttavia, secondo la sua fonte originale , ci sono alcuni problemi con questo codice.

  • L'eccezione verrà sollevata solo durante l'esecuzione del bytecode python. Se il thread chiama una funzione di blocco nativa / incorporata, l'eccezione verrà sollevata solo quando l'esecuzione torna al codice Python.
    • C'è anche un problema se la funzione integrata chiama internamente PyErr_Clear (), che annullerebbe effettivamente l'eccezione in sospeso. Puoi provare a sollevarlo di nuovo.
  • Solo i tipi di eccezione possono essere sollevati in modo sicuro. Le istanze di eccezione possono causare comportamenti imprevisti e pertanto sono limitate.
  • Ho chiesto di esporre questa funzione nel modulo thread incorporato, ma poiché ctypes è diventato una libreria standard (a partire da 2.5), e questa
    funzionalità non è probabilmente indipendente dall'implementazione, potrebbe non essere
    esposta.

0

Questo sembra funzionare con pywin32 su Windows 7

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()

0

Pieter Hintjens - uno dei fondatori del progetto ØMQ - afferma, usando ØMQ ed evitando primitive di sincronizzazione come blocchi, mutex, eventi ecc., È il modo più sicuro e sicuro per scrivere programmi multi-thread:

http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ

Ciò include dire a un thread figlio che dovrebbe annullare il suo lavoro. Ciò avverrebbe dotando il thread di un socket ØMQ e eseguendo il polling su quel socket per un messaggio che dice che dovrebbe essere annullato.

Il collegamento fornisce anche un esempio sul codice Python multi-thread con ØMQ.


0

Supponendo che tu voglia avere più thread della stessa funzione, questa è IMHO l'implementazione più semplice per fermarne una per id:

import time
from threading import Thread

def doit(id=0):
    doit.stop=0
    print("start id:%d"%id)
    while 1:
        time.sleep(1)
        print(".")
        if doit.stop==id:
            doit.stop=0
            break
    print("end thread %d"%id)

t5=Thread(target=doit, args=(5,))
t6=Thread(target=doit, args=(6,))

t5.start() ; t6.start()
time.sleep(2)
doit.stop =5  #kill t5
time.sleep(2)
doit.stop =6  #kill t6

La cosa bella è qui, puoi avere più funzioni uguali e diverse e fermarle tutte functionname.stop

Se si desidera avere un solo thread della funzione, non è necessario ricordare l'id. Basta fermarsi, se doit.stop> 0.


0

Solo per sviluppare l'idea di @ SCB (che era esattamente quello di cui avevo bisogno) per creare una sottoclasse KillableThread con una funzione personalizzata:

from threading import Thread, Event

class KillableThread(Thread):
    def __init__(self, sleep_interval=1, target=None, name=None, args=(), kwargs={}):
        super().__init__(None, target, name, args, kwargs)
        self._kill = Event()
        self._interval = sleep_interval
        print(self._target)

    def run(self):
        while True:
            # Call custom function with arguments
            self._target(*self._args)

        # If no kill signal is set, sleep for the interval,
        # If kill signal comes in while sleeping, immediately
        #  wake up and handle
        is_killed = self._kill.wait(self._interval)
        if is_killed:
            break

    print("Killing Thread")

def kill(self):
    self._kill.set()

if __name__ == '__main__':

    def print_msg(msg):
        print(msg)

    t = KillableThread(10, print_msg, args=("hello world"))
    t.start()
    time.sleep(6)
    print("About to kill thread")
    t.kill()

Naturalmente, come con @SBC, il thread non aspetta di eseguire un nuovo loop per arrestarsi. In questo esempio, vedresti il ​​messaggio "Killing Thread" stampato subito dopo "About to kill thread" invece di attendere altri 4 secondi per completare il thread (dato che abbiamo già dormito per 6 secondi).

Il secondo argomento nel costruttore di KillableThread è la tua funzione personalizzata (print_msg qui). Gli argomenti Args sono gli argomenti che verranno utilizzati quando si chiama la funzione (("ciao mondo")) qui.


0

Come menzionato nella risposta di @ Kozyarchuk , l'installazione di trace funziona. Poiché questa risposta non conteneva alcun codice, ecco un esempio funzionante e pronto per l'uso:

import sys, threading, time 

class TraceThread(threading.Thread): 
    def __init__(self, *args, **keywords): 
        threading.Thread.__init__(self, *args, **keywords) 
        self.killed = False
    def start(self): 
        self._run = self.run 
        self.run = self.settrace_and_run
        threading.Thread.start(self) 
    def settrace_and_run(self): 
        sys.settrace(self.globaltrace) 
        self._run()
    def globaltrace(self, frame, event, arg): 
        return self.localtrace if event == 'call' else None
    def localtrace(self, frame, event, arg): 
        if self.killed and event == 'line': 
            raise SystemExit() 
        return self.localtrace 

def f(): 
    while True: 
        print('1') 
        time.sleep(2)
        print('2') 
        time.sleep(2)
        print('3') 
        time.sleep(2)

t = TraceThread(target=f) 
t.start() 
time.sleep(2.5) 
t.killed = True

Si ferma dopo aver stampato 1e 2. 3non è stampato.


-1

È possibile eseguire il comando in un processo e quindi ucciderlo utilizzando l'ID processo. Avevo bisogno di sincronizzare tra due thread uno dei quali non ritorna da solo.

processIds = []

def executeRecord(command):
    print(command)

    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    processIds.append(process.pid)
    print(processIds[0])

    #Command that doesn't return by itself
    process.stdout.read().decode("utf-8")
    return;


def recordThread(command, timeOut):

    thread = Thread(target=executeRecord, args=(command,))
    thread.start()
    thread.join(timeOut)

    os.kill(processIds.pop(), signal.SIGINT)

    return;

-1

Inizia il thread secondario con setDaemon (True).

def bootstrap(_filename):
    mb = ModelBootstrap(filename=_filename) # Has many Daemon threads. All get stopped automatically when main thread is stopped.

t = threading.Thread(target=bootstrap,args=('models.conf',))
t.setDaemon(False)

while True:
    t.start()
    time.sleep(10) # I am just allowing the sub-thread to run for 10 sec. You can listen on an event to stop execution.
    print('Thread stopped')
    break

-2

Questa è una cattiva risposta, vedere i commenti

Ecco come farlo:

from threading import *

...

for thread in enumerate():
    if thread.isAlive():
        try:
            thread._Thread__stop()
        except:
            print(str(thread.getName()) + ' could not be terminated'))

Dagli qualche secondo quindi il tuo thread dovrebbe essere fermato. Controlla anche ilthread._Thread__delete() metodo.

Consiglierei un thread.quit()metodo per comodità. Ad esempio, se hai un socket nel tuo thread, ti consiglio di creare un quit()metodo nella tua classe handle-handle, terminare il socket, quindi eseguire un thread._Thread__stop()interno al tuo quit().


Ho dovuto usare self._Thread__stop () all'interno del mio threading. Oggetto thread per farlo fermare. Non capisco perché un semplice self.join () come questo esempio ( code.activestate.com/recipes/65448-thread-control-idiom ) non funziona.
Harijay,

12
Maggiori dettagli su "questo non impedisce davvero una discussione" sarebbero utili.
2371,

19
Fondamentalmente, chiamare il metodo _Thread__stop non ha alcun effetto oltre a dire a Python che il thread è stato interrotto. Può effettivamente continuare a funzionare. Vedi gist.github.com/2787191 per un esempio.
Bluehorn,

35
Questo è chiaramente sbagliato. contrassegna_Thread__stop() semplicemente un thread come interrotto , in realtà non interrompe il thread! Non farlo mai. Avere una lettura .
dotancohen,

-2

Se hai davvero bisogno della possibilità di terminare un'attività secondaria, usa un'implementazione alternativa. multiprocessinged gevententrambi supportano l'uccisione indiscriminata di un "thread".

Il threading di Python non supporta la cancellazione. Non provarci nemmeno. È molto probabile che il tuo codice blocchi, corrompa o perda la memoria o abbia altri effetti "interessanti" difficili da debug involontari che si verificano raramente e non deterministicamente.


2
... e sì, so che entrambi non sono strettamente "threading", ma funzionano entrambi se il codice si adatta (o può essere adattato) al loro modello.
Matthias Urlichs,
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.