logo

Python | Različni načini za prekinitev niti

Na splošno se nenadno uničenje niti šteje za slabo programsko prakso. Nenadna prekinitev niti lahko pusti kritični vir, ki ga je treba pravilno zapreti, odprt. Toda morda boste želeli uničiti nit, ko mine določeno časovno obdobje ali se ustvari prekinitev. Obstajajo različne metode, s katerimi lahko ubijete nit v pythonu.

  • Vzbujanje izjem v niti python
  • Nastavitev/ponastavitev zaustavitvene zastavice
  • Uporaba sledi za prekinitev niti
  • Uporaba večprocesnega modula za prekinitev niti
  • Uničenje niti Python z nastavitvijo kot demona
  • Uporaba skrite funkcije _stop()

Vzbujanje izjem v niti python:
Ta metoda uporablja funkcijo PyThreadState_SetAsyncExc() sprožiti izjemo v niti. Na primer,



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

>

>

Ko zaženemo zgornjo kodo v stroju in boste opazili, da se ciljna funkcija run() konča takoj, ko se pokliče funkcija raise_exception(). To je zato, ker takoj, ko se sproži izjema, nadzor programa skoči iz poskusnega bloka in funkcija run() se prekine. Po tem lahko pokličete funkcijo join(), da prekinete nit. Če funkcije run_exception() ni, se ciljna funkcija run() izvaja večno in funkcija join() ni nikoli poklicana, da bi prekinila nit.

Nastavitev/ponastavitev zaustavitvene zastavice:
Da bi uničili nit, lahko razglasimo zaustavitveno zastavico in to zastavico bo nit občasno preverila. Na primer

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

>

komponente robota
>

V zgornji kodi, takoj ko je nastavljena globalna spremenljivka stop_threads, se ciljna funkcija run() konča in nit t1 je mogoče uničiti z uporabo t1.join(). Lahko pa se vzdržimo uporabe globalne spremenljivke zaradi določenih razlogov. Za te situacije je mogoče posredovati funkcijske objekte, da zagotovijo podobno funkcionalnost, kot je prikazano spodaj.

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

>

>

Funkcijski objekt, posredovan v zgornji kodi, vedno vrne vrednost lokalne spremenljivke stop_threads. Ta vrednost se preveri v funkciji run() in takoj ko se stop_threads ponastavi, se funkcija run() konča in nit je mogoče uničiti.

Uporaba sledi za prekinitev niti:
Ta metoda deluje z namestitvijo sledi v vsaki niti. Vsaka sled se konča ob zaznavi nekega dražljaja ali zastavice, s čimer takoj prekine povezano nit. Na primer

Python3


pretvorba niza v objekt json



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

>

>

V tej kodi je start() nekoliko spremenjen, da nastavi funkcijo sledenja sistema z uporabo settrace() . Lokalna funkcija sledenja je definirana tako, da se vsakič, ko je nastavljena zastavica za uničenje (killed) zadevne niti, ob izvedbi naslednje vrstice kode sproži izjema SystemExit, ki konča izvajanje ciljne funkcije func. Zdaj je mogoče nit uničiti s funkcijo join().

Uporaba večprocesnega modula za prekinitev niti:
Večprocesni modul Pythona vam omogoča ustvarjanje procesov na podoben način kot ustvarjanje niti z uporabo modula za navoje. Vmesnik modula za večnitnost je podoben vmesniku modula za navoje. Na primer, v dani kodi smo ustvarili tri niti (procese), ki štejejo od 1 do 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()>

>

>

Funkcionalnost zgornje kode je mogoče implementirati tudi z uporabo večprocesnega modula na podoben način, z zelo malo spremembami. Oglejte si spodnjo kodo.

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

>

>

Čeprav je vmesnik obeh modulov podoben, imata modula zelo različni izvedbi. Vse niti si delijo globalne spremenljivke, medtem ko so procesi popolnoma ločeni drug od drugega. Zato so procesi ubijanja veliko varnejši v primerjavi z uničevanjem niti. Razred Process ima metodo, prekiniti () , za ubijanje procesa. Zdaj pa se vrnimo k začetni težavi. Recimo, da v zgornji kodi želimo uničiti vse procese po preteku 0,03 s. Ta funkcionalnost je dosežena z uporabo večprocesnega modula v naslednji kodi.

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

>

>

Čeprav imata oba modula različne izvedbe. Ta funkcionalnost, ki jo zagotavlja večprocesni modul v zgornji kodi, je podobna ubijanju niti. Zato se lahko večprocesni modul uporablja kot preprost alternativa kadarkoli moramo izvesti ubijanje niti v Pythonu.

Uničenje niti Python z nastavitvijo kot demona:
Daemon niti so tiste niti, ki se uničijo, ko glavni program zapre. Na primer

Python3

parameter v lupinskem skriptu




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

>

>

Upoštevajte, da nit t1 ostane živa in prepreči glavnemu programu izhod prek sys.exit(). V Pythonu vsaka živa nit, ki ni demon, blokira glavni program za izhod. Same demonske niti pa so uničene takoj, ko glavni program zapre. Z drugimi besedami, takoj ko glavni program zapre, so vse demonske niti uničene. Če želite razglasiti nit kot demon, nastavimo argument ključne besede daemon kot True. Na primer, v dani kodi prikazuje lastnost demonskih niti.

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

>

>

Upoštevajte, da se nit t1 prekine takoj, ko glavni program zapre. Ta metoda se je izkazala za izjemno uporabno v primerih, ko je mogoče prekinitev programa uporabiti za sprožitev uničenja niti. Upoštevajte, da se v Pythonu glavni program konča takoj, ko so vse nedemonske niti mrtve, ne glede na število živih demonskih niti. Zato viri, ki jih hranijo te demonske niti, kot so odprte datoteke, transakcije baze podatkov itd., morda ne bodo pravilno sproščeni. Začetna nit nadzora v programu python ni demonska nit. Nasilno prekinitev niti ni priporočljiva, razen če je zagotovo znano, da to ne bo povzročilo puščanja ali zastojev.
Uporaba skrite funkcije _stop():
Za prekinitev niti uporabimo skrito funkcijo _stop(). Ta funkcija ni dokumentirana, vendar lahko izgine v naslednji različici pythona.

lisica proti volku

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

>

>

Opomba: Zgornje metode morda ne bodo delovale v določeni situaciji, ker python ne nudi nobene neposredne metode za prekinitev niti.