Obecně je náhlé zabíjení vláken považováno za špatnou programátorskou praxi. Náhlé zabití vlákna může zanechat kritický zdroj, který musí být správně uzavřen, otevřený. Ale možná budete chtít ukončit vlákno, jakmile uplyne určité časové období nebo bylo vygenerováno nějaké přerušení. Existují různé metody, kterými můžete zabít vlákno v pythonu.
- Vyvolávání výjimek ve vláknu pythonu
- Nastavit/resetovat příznak zastavení
- Použití stop k zabití vláken
- Použití modulu multiprocessingu k ukončení vláken
- Zabití vlákna Pythonu jeho nastavením jako démona
- Použití skryté funkce _stop()
Vyvolání výjimek ve vláknu pythonu:
Tato metoda využívá funkci PyThreadState_SetAsyncExc() vyvolat výjimku ve vláknu. Například,
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()> |
>
>
Když spustíme výše uvedený kód na počítači a všimnete si, že jakmile je zavolána funkce raise_exception(), cílová funkce run() skončí. Je to proto, že jakmile je vyvolána výjimka, řízení programu vyskočí z bloku try a funkce run() je ukončena. Poté lze zavolat funkci join() k ukončení vlákna. V nepřítomnosti funkce run_exception(), cílová funkce run() běží navždy a funkce join() není nikdy volána, aby ukončila vlákno.
Nastavit/resetovat příznak zastavení:
Abychom zabili vlákna, můžeme deklarovat stop příznak a tento příznak bude vláknem občas kontrolován. Například
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'>)> |
>
řádek příkazu autocad
>
Ve výše uvedeném kódu, jakmile je nastavena globální proměnná stop_threads, cílová funkce run() skončí a vlákno t1 lze zabít pomocí t1.join(). Ale z určitých důvodů se může zdržet použití globální proměnné. V těchto situacích lze předat funkční objekty, aby poskytovaly podobnou funkčnost, jak je uvedeno níže.
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()> |
>
>
Funkční objekt předaný ve výše uvedeném kódu vždy vrací hodnotu lokální proměnné stop_threads. Tato hodnota je zkontrolována ve funkci run() a jakmile se stop_threads resetuje, funkce run() skončí a vlákno lze zabít.
Použití tras k zabití vláken:
Tato metoda funguje instalací stopy v každém vlákně. Každá stopa se ukončí při detekci nějakého podnětu nebo příznaku, čímž okamžitě zabije související vlákno. Například
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'>)> |
>
>
V tomto kódu je start() mírně upraven tak, aby nastavil funkci trasování systému pomocí settrace() . Funkce lokálního trasování je definována tak, že kdykoli je nastaven příznak zabití (killed) příslušného vlákna, je vyvolána výjimka SystemExit při spuštění dalšího řádku kódu, která ukončí provádění cílové funkce func. Nyní lze vlákno zabít pomocí join().
Použití modulu multiprocessingu k ukončení vláken:
Modul multiprocessingu Pythonu vám umožňuje vytvářet procesy podobným způsobem, jakým vytváříte vlákna pomocí modulu pro vytváření vláken. Rozhraní modulu vícevláknového zpracování je podobné rozhraní modulu zpracování vláken. Například v daném kódu jsme vytvořili tři vlákna (procesy), které se počítají od 1 do 9.
Python3
převod řetězce na objekt json
# 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()> |
>
>
Funkčnost výše uvedeného kódu lze také implementovat pomocí modulu multiprocessingu podobným způsobem s velmi malými změnami. Viz kód uvedený níže.
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()> |
>
>
Přestože je rozhraní obou modulů podobné, mají tyto dva moduly velmi odlišné implementace. Všechna vlákna sdílejí globální proměnné, zatímco procesy jsou od sebe zcela oddělené. Procesy zabíjení jsou tedy mnohem bezpečnější ve srovnání se zabíjením vláken. Třída Process poskytuje metodu, vypovědět() , zabít proces. Nyní se vraťme k původnímu problému. Předpokládejme, že ve výše uvedeném kódu chceme zabít všechny procesy po uplynutí 0,03 s. Této funkce je dosaženo pomocí modulu multiprocessing v následujícím kódu.
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()> |
>
>
Ačkoli tyto dva moduly mají různé implementace. Tato funkce poskytovaná modulem multiprocessing ve výše uvedeném kódu je podobná zabíjení vláken. Modul multiprocessingu lze tedy použít jako jednoduchý alternativní kdykoli jsme povinni implementovat zabíjení vláken v Pythonu.
Zabití vlákna Pythonu jeho nastavením jako démona:
Vlákna démonů jsou vlákna, která jsou zabita při ukončení hlavního programu. Například
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()> |
>
>
Všimněte si, že vlákno t1 zůstává aktivní a brání hlavnímu programu ukončit se pomocí sys.exit(). V Pythonu každé aktivní vlákno bez démona blokuje ukončení hlavního programu. Zatímco samotná vlákna démona jsou zabita, jakmile hlavní program skončí. Jinými slovy, jakmile hlavní program skončí, všechna vlákna démona jsou zabita. Abychom vlákno deklarovali jako démona, nastavíme klíčové slovo argument daemon na True. Například v daném kódu demonstruje vlastnost daemon threads.
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()> |
>
>
Všimněte si, že jakmile hlavní program skončí, vlákno t1 je zabito. Tato metoda se ukazuje jako mimořádně užitečná v případech, kdy lze ukončení programu použít ke spuštění zabíjení vláken. Všimněte si, že v Pythonu se hlavní program ukončí, jakmile jsou všechna vlákna bez démona mrtvá, bez ohledu na počet živých vláken démona. Prostředky držené těmito vlákny démonů, jako jsou otevřené soubory, databázové transakce atd., proto nemusí být uvolněny správně. Počáteční vlákno řízení v programu python není vlákno démona. Násilné zabíjení vlákna se nedoporučuje, pokud není jisté, že to nezpůsobí žádné úniky nebo uváznutí.
Použití skryté funkce _stop() :
Abychom zabili vlákno, používáme skrytou funkci _stop() tato funkce není zdokumentována, ale v příští verzi pythonu může zmizet.
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()> |
had python vs anakonda
>
>
Poznámka: Výše uvedené metody nemusí v určitých situacích fungovat, protože python neposkytuje žádnou přímou metodu pro zabíjení vláken.