logo

Pythona | Różne sposoby zabicia wątku

Ogólnie rzecz biorąc, nagłe zabijanie wątków jest uważane za złą praktykę programistyczną. Nagłe zabicie wątku może pozostawić kluczowy zasób, który należy odpowiednio zamknąć i otworzyć. Ale możesz chcieć zabić wątek po upływie określonego czasu lub wygenerowaniu przerwania. Istnieją różne metody zabicia wątku w Pythonie.

  • Zgłaszanie wyjątków w wątku Pythona
  • Ustaw/zresetuj flagę zatrzymania
  • Używanie śladów do zabijania wątków
  • Używanie modułu wieloprocesorowego do zabijania wątków
  • Zabijanie wątku Pythona poprzez ustawienie go jako demona
  • Korzystanie z ukrytej funkcji _stop()

Zgłaszanie wyjątków w wątku Pythona:
Ta metoda wykorzystuje funkcję PyThreadState_SetAsyncExc() aby zgłosić wyjątek w wątku a. Na przykład,



Python3




co to jest gb





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

>

>

Kiedy uruchomimy powyższy kod na maszynie i zauważysz, że zaraz po wywołaniu funkcji raise_exception() funkcja docelowa run() kończy się. Dzieje się tak dlatego, że gdy tylko zostanie zgłoszony wyjątek, sterowanie programem wyskakuje z bloku try, a funkcja run() zostaje zakończona. Następnie można wywołać funkcję Join() w celu zakończenia wątku. W przypadku braku funkcji run_exception(), funkcja docelowa run() będzie działać w nieskończoność, a funkcja Join() nigdy nie zostanie wywołana w celu zakończenia wątku.

Ustaw/Zresetuj flagę zatrzymania:
Aby zabić wątek, możemy zadeklarować flagę stopu i ta flaga będzie od czasu do czasu sprawdzana przez wątek. Na przykład

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

>

>

W powyższym kodzie, gdy tylko zmienna globalna stop_threads zostanie ustawiona, funkcja docelowa run() kończy się, a wątek t1 można zakończyć za pomocą t1.join(). Można jednak powstrzymać się od używania zmiennej globalnej z pewnych powodów. W takich sytuacjach można przekazać obiekty funkcyjne, aby zapewnić podobną funkcjonalność, jak pokazano poniżej.

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

>

rdzeń Java, Java

>

Obiekt funkcji przekazany w powyższym kodzie zawsze zwraca wartość zmiennej lokalnej stop_threads. Wartość ta jest sprawdzana w funkcji run() i po zresetowaniu stop_threads funkcja run() kończy się i wątek może zostać zabity.

Używanie śladów do zabijania wątków:
Ta metoda działa poprzez instalację ślady w każdym wątku. Każdy ślad kończy się po wykryciu jakiegoś bodźca lub flagi, w ten sposób natychmiastowo zabijając powiązany wątek. Na przykład

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

>

>

W tym kodzie funkcja start() została nieco zmodyfikowana, aby ustawić funkcję śledzenia systemu za pomocą settrace() . Lokalna funkcja śledzenia jest zdefiniowana w taki sposób, że za każdym razem, gdy ustawiona jest flaga zabicia (killed) odpowiedniego wątku, po wykonaniu następnego wiersza kodu zgłaszany jest wyjątek SystemExit, co kończy wykonywanie funkcji docelowej func. Teraz wątek można zabić za pomocą metody Join().

Używanie modułu wieloprocesorowego do zabijania wątków:
Moduł wieloprocesowy języka Python umożliwia tworzenie procesów w podobny sposób, w jaki tworzy się wątki za pomocą modułu wątków. Interfejs modułu wielowątkowości jest podobny do interfejsu modułu wątków. Na przykład w danym kodzie utworzyliśmy trzy wątki (procesy), które liczą się 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()>

>

>

Funkcjonalność powyższego kodu można również zaimplementować wykorzystując w podobny sposób moduł wieloprocesorowy, z niewielkimi zmianami. Zobacz kod podany poniżej.

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

mnożenie macierzy w c
>

>

Chociaż interfejs obu modułów jest podobny, oba moduły mają bardzo różne implementacje. Wszystkie wątki mają wspólne zmienne globalne, natomiast procesy są od siebie całkowicie oddzielone. Dlatego procesy zabijania są znacznie bezpieczniejsze w porównaniu do zabijania wątków. Klasa Process posiada metodę, zakończyć() , aby zabić proces. A teraz wracając do początkowego problemu. Załóżmy, że w powyższym kodzie chcemy zakończyć wszystkie procesy po upływie 0,03 s. Tę funkcjonalność osiąga się za pomocą modułu przetwarzania wieloprocesowego w poniższym kodzie.

leksykograficznie

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

>

>

Chociaż te dwa moduły mają różne implementacje. Ta funkcjonalność zapewniana przez moduł wieloprocesorowy w powyższym kodzie jest podobna do zabijania wątków. Dlatego moduł wieloprocesorowy może być używany jako prosty alternatywny ilekroć jesteśmy zobowiązani do wdrożenia zabijania wątków w Pythonie.

Zabijanie wątku Pythona poprzez ustawienie go jako demona:
Wątki demona to wątki, które są zabijane po wyjściu głównego programu. Na przykład

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

>

>

Zauważ, że wątek t1 pozostaje aktywny i uniemożliwia wyjście z programu głównego poprzez sys.exit(). W Pythonie każdy żywy wątek niebędący demonem blokuje wyjście głównego programu. Natomiast same wątki demonów są zabijane zaraz po wyjściu głównego programu. Innymi słowy, gdy tylko główny program zakończy działanie, wszystkie wątki demona zostaną zabite. Aby zadeklarować wątek jako demona, ustawiamy argument słowa kluczowego daemon na True. Na przykład w podanym kodzie demonstruje właściwość wątków demona.

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

>

>

Zauważ, że zaraz po zamknięciu programu głównego wątek t1 zostaje zabity. Metoda ta okazuje się niezwykle użyteczna w przypadkach, gdy zakończenie programu może zostać wykorzystane do wywołania zabijania wątków. Należy zauważyć, że w Pythonie program główny kończy działanie, gdy tylko wszystkie wątki inne niż demony przestaną działać, niezależnie od liczby aktywnych wątków demonów. Dlatego zasoby przechowywane przez te wątki demona, takie jak otwarte pliki, transakcje w bazie danych itp., mogą nie zostać prawidłowo zwolnione. Początkowy wątek sterujący w programie Pythona nie jest wątkiem demona. Nie zaleca się zabijania wątku na siłę, chyba że wiadomo na pewno, że nie spowoduje to żadnych wycieków ani zakleszczeń.
Używanie ukrytej funkcji _stop() :
Aby zabić wątek, używamy ukrytej funkcji _stop(). Ta funkcja nie jest udokumentowana, ale może zniknąć w następnej wersji Pythona.

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

>

oni są piosenkarzami
>

Notatka: Powyższe metody mogą nie działać w tej czy innej sytuacji, ponieważ Python nie udostępnia żadnej bezpośredniej metody zabijania wątków.