In generale, interrompere bruscamente i thread è considerata una cattiva pratica di programmazione. Uccidere improvvisamente un thread potrebbe lasciare aperta una risorsa critica che deve essere chiusa correttamente. Ma potresti voler terminare un thread una volta trascorso un periodo di tempo specifico o una volta generato un interrupt. Esistono vari metodi con cui puoi terminare un thread in Python.
- Sollevare eccezioni in un thread Python
- Imposta/reimposta il flag di arresto
- Utilizzo delle tracce per terminare i thread
- Utilizzo del modulo multiprocessing per terminare i thread
- Uccisione del thread Python impostandolo come demone
- Utilizzando una funzione nascosta _stop()
Sollevare eccezioni in un thread Python:
Questo metodo utilizza la funzione PyThreadState_SetAsyncExc() per sollevare un'eccezione nel thread a. Per esempio,
Python3
# Python program raising> # exceptions in a python> # thread> import> threading> import> ctypes> import> time> > class> thread_with_exception(threading.Thread):> > def> __init__(> self> , name):> > threading.Thread.__init__(> self> )> > self> .name> => name> > > def> run(> self> ):> > # target function of the thread class> > try> :> > while> True> :> > print> (> 'running '> +> self> .name)> > finally> :> > print> (> 'ended'> )> > > def> get_id(> self> ):> > # returns id of the respective thread> > if> hasattr> (> self> ,> '_thread_id'> ):> > return> self> ._thread_id> > for> id> , thread> in> threading._active.items():> > if> thread> is> self> :> > return> id> > > def> raise_exception(> self> ):> > thread_id> => self> .get_id()> > res> => ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,> > ctypes.py_object(SystemExit))> > if> res>> 1> :> > ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,> 0> )> > print> (> 'Exception raise failure'> )> > t1> => thread_with_exception(> 'Thread 1'> )> t1.start()> time.sleep(> 2> )> t1.raise_exception()> t1.join()> |
>
>
Quando eseguiamo il codice sopra in una macchina e noterai, non appena viene chiamata la funzione raise_exception(), la funzione target run() termina. Questo perché non appena viene sollevata un'eccezione, il controllo del programma salta fuori dal blocco try e la funzione run() viene terminata. Dopodiché è possibile chiamare la funzione join() per terminare il thread. In assenza della funzione run_exception(), la funzione target run() continua a funzionare per sempre e la funzione join() non viene mai chiamata per terminare il thread.
Imposta/Reimposta il flag di arresto:
Per uccidere un thread, possiamo dichiarare un flag di stop e questo flag verrà controllato occasionalmente dal thread. Per esempio
Python3
# Python program showing> # how to kill threads> # using set/reset stop> # flag> import> threading> import> time> def> run():> > while> True> :> > print> (> 'thread running'> )> > global> stop_threads> > if> stop_threads:> > break> stop_threads> => False> t1> => threading.Thread(target> => run)> t1.start()> time.sleep(> 1> )> stop_threads> => True> t1.join()> print> (> 'thread killed'> )> |
>
>
Nel codice precedente, non appena viene impostata la variabile globale stop_threads, la funzione target run() termina e il thread t1 può essere terminato utilizzando t1.join(). Ma è possibile astenersi dall'utilizzare la variabile globale per determinati motivi. Per tali situazioni, è possibile passare oggetti funzione per fornire una funzionalità simile come mostrato di seguito.
Python3
# Python program killing> # threads using stop> # flag> import> threading> import> time> def> run(stop):> > while> True> :> > print> (> 'thread running'> )> > if> stop():> > break> > def> main():> > stop_threads> => False> > t1> => threading.Thread(target> => run, args> => (> lambda> : stop_threads, ))> > t1.start()> > time.sleep(> 1> )> > stop_threads> => True> > t1.join()> > print> (> 'thread killed'> )> main()> |
>
>
L'oggetto funzione passato nel codice precedente restituisce sempre il valore della variabile locale stop_threads. Questo valore viene controllato nella funzione run() e non appena stop_threads viene reimpostato, la funzione run() termina e il thread può essere terminato.
Utilizzo delle tracce per terminare i thread:
Questo metodo funziona installando tracce in ogni thread. Ogni traccia termina se stessa al rilevamento di qualche stimolo o flag, uccidendo così istantaneamente il thread associato. Per esempio
Python3
# Python program using> # traces to kill threads> import> sys> import> trace> import> threading> import> time> class> thread_with_trace(threading.Thread):> > def> __init__(> self> ,> *> args,> *> *> keywords):> > threading.Thread.__init__(> self> ,> *> args,> *> *> keywords)> > self> .killed> => False> > def> start(> self> ):> > self> .__run_backup> => self> .run> > self> .run> => self> .__run> > threading.Thread.start(> self> )> > def> __run(> self> ):> > sys.settrace(> self> .globaltrace)> > self> .__run_backup()> > self> .run> => self> .__run_backup> > def> globaltrace(> self> , frame, event, arg):> > if> event> => => 'call'> :> > return> self> .localtrace> > else> :> > return> None> > def> localtrace(> self> , frame, event, arg):> > if> self> .killed:> > if> event> => => 'line'> :> > raise> SystemExit()> > return> self> .localtrace> > def> kill(> self> ):> > self> .killed> => True> def> func():> > while> True> :> > print> (> 'thread running'> )> t1> => thread_with_trace(target> => func)> t1.start()> time.sleep(> 2> )> t1.kill()> t1.join()> if> not> t1.isAlive():> > print> (> 'thread killed'> )> |
>
>
In questo codice, start() viene leggermente modificato per impostare la funzione di traccia del sistema utilizzando settraccia() . La funzione di traccia locale è definita in modo tale che, ogni volta che viene impostato il kill flag (killed) del rispettivo thread, viene sollevata un'eccezione SystemExit all'esecuzione della riga di codice successiva, che termina l'esecuzione della funzione target func. Ora il thread può essere terminato con join().
Utilizzando il modulo multiprocessing per terminare i thread:
Il modulo multiprocessing di Python ti consente di generare processi nello stesso modo in cui generi thread usando il modulo threading. L'interfaccia del modulo multithreading è simile a quella del modulo threading. Ad esempio, in un dato codice abbiamo creato tre thread (processi) che contano da 1 a 9.
Python3
# Python program creating> # three threads> import> threading> import> time> # counts from 1 to 9> def> func(number):> > for> i> in> range> (> 1> ,> 10> ):> > time.sleep(> 0.01> )> > print> (> 'Thread '> +> str> (number)> +> ': prints '> +> str> (number> *> i))> # creates 3 threads> for> i> in> range> (> 0> ,> 3> ):> > thread> => threading.Thread(target> => func, args> => (i,))> > thread.start()> |
>
>
La funzionalità del codice sopra può essere implementata anche utilizzando il modulo multiprocessing in modo simile, con pochissime modifiche. Vedere il codice riportato di seguito.
Python3
# Python program creating> # thread using multiprocessing> # module> import> multiprocessing> import> time> def> func(number):> > for> i> in> range> (> 1> ,> 10> ):> > time.sleep(> 0.01> )> > print> (> 'Processing '> +> str> (number)> +> ': prints '> +> str> (number> *> i))> for> i> in> range> (> 0> ,> 3> ):> > process> => multiprocessing.Process(target> => func, args> => (i,))> > process.start()> |
funzione anonima Java
>
>
Sebbene l'interfaccia dei due moduli sia simile, i due moduli hanno implementazioni molto diverse. Tutti i thread condividono variabili globali, mentre i processi sono completamente separati gli uni dagli altri. Pertanto, i processi di kill sono molto più sicuri rispetto ai thread di kill. Alla classe Process viene fornito un metodo, terminare() , per terminare un processo. Ora, torniamo al problema iniziale. Supponiamo che nel codice sopra, vogliamo terminare tutti i processi dopo che sono trascorsi 0,03 secondi. Questa funzionalità si ottiene utilizzando il modulo multiprocessing nel codice seguente.
Python3
# Python program killing> # a thread using multiprocessing> # module> import> multiprocessing> import> time> def> func(number):> > for> i> in> range> (> 1> ,> 10> ):> > time.sleep(> 0.01> )> > print> (> 'Processing '> +> str> (number)> +> ': prints '> +> str> (number> *> i))> # list of all processes, so that they can be killed afterwards> all_processes> => []> for> i> in> range> (> 0> ,> 3> ):> > process> => multiprocessing.Process(target> => func, args> => (i,))> > process.start()> > all_processes.append(process)> # kill all processes after 0.03s> time.sleep(> 0.03> )> for> process> in> all_processes:> > process.terminate()> |
>
>
Sebbene i due moduli abbiano implementazioni diverse. Questa funzionalità fornita dal modulo multiprocessing nel codice precedente è simile all'eliminazione dei thread. Quindi, il modulo multiprocessing può essere utilizzato come semplice alternativa ogni volta che ci viene richiesto di implementare l'uccisione dei thread in Python.
Uccisione del thread Python impostandolo come demone:
Thread del demone sono quei thread che vengono terminati all'uscita del programma principale. Per esempio
Python3
import> threading> import> time> import> sys> def> func():> > while> True> :> > time.sleep(> 0.5> )> > print> (> 'Thread alive, and it won't die on program termination'> )> t1> => threading.Thread(target> => func)> t1.start()> time.sleep(> 2> )> sys.exit()> |
>
>
Si noti che il thread t1 rimane attivo e impedisce al programma principale di uscire tramite sys.exit(). In Python, qualsiasi thread attivo non demone blocca l'uscita del programma principale. Al contrario, i thread stessi del demone vengono terminati non appena il programma principale termina. In altre parole, non appena il programma principale esce, tutti i thread del demone vengono terminati. Per dichiarare un thread come demone, impostiamo l'argomento chiave demone come True. Ad esempio nel codice fornito viene dimostrata la proprietà dei thread del demone.
Python3
# Python program killing> # thread using daemon> import> threading> import> time> import> sys> def> func():> > while> True> :> > time.sleep(> 0.5> )> > print> (> 'Thread alive, but it will die on program termination'> )> t1> => threading.Thread(target> => func)> t1.daemon> => True> t1.start()> time.sleep(> 2> )> sys.exit()> |
>
>
Si noti che, non appena il programma principale esce, il thread t1 viene terminato. Questo metodo si rivela estremamente utile nei casi in cui la chiusura del programma può essere utilizzata per innescare l'uccisione dei thread. Si noti che in Python, il programma principale termina non appena tutti i thread non demoniaci sono morti, indipendentemente dal numero di thread demoniaci attivi. Pertanto, le risorse conservate da questi thread del demone, come file aperti, transazioni di database, ecc. potrebbero non essere rilasciate correttamente. Il thread di controllo iniziale in un programma Python non è un thread daemon. Non è consigliabile terminare forzatamente un thread a meno che non si sappia con certezza che ciò non causerà perdite o deadlock.
Utilizzando una funzione nascosta _stop() :
Per terminare un thread, utilizziamo la funzione nascosta _stop() questa funzione non è documentata ma potrebbe scomparire nella prossima versione di Python.
Python3
# Python program killing> # a thread using ._stop()> # function> import> time> import> threading> class> MyThread(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> (MyThread,> self> ).__init__(> *> args,> *> *> kwargs)> > self> ._stop> => threading.Event()> > # function using _stop function> > def> stop(> self> ):> > self> ._stop.> set> ()> > def> stopped(> self> ):> > return> self> ._stop.isSet()> > def> run(> self> ):> > while> True> :> > if> self> .stopped():> > return> > print> (> 'Hello, world!'> )> > time.sleep(> 1> )> t1> => MyThread()> t1.start()> time.sleep(> 5> )> t1.stop()> t1.join()> |
>
>
Nota: I metodi sopra indicati potrebbero non funzionare in una situazione o nell'altra, perché Python non fornisce alcun metodo diretto per terminare i thread.