Czy można zakończyć działający wątek bez ustawiania / sprawdzania flag / semaforów / itp.?
Czy można zakończyć działający wątek bez ustawiania / sprawdzania flag / semaforów / itp.?
Odpowiedzi:
Zasadniczo nagłe zabicie wątku w Pythonie i dowolnym języku jest złym wzorcem. Pomyśl o następujących przypadkach:
Dobrym sposobem na poradzenie sobie z tym, jeśli możesz sobie na to pozwolić (jeśli zarządzasz własnymi wątkami), jest ustawienie flagi exit_request, którą każdy wątek sprawdza w regularnych odstępach czasu, aby sprawdzić, czy nadszedł czas na zakończenie.
Na przykład:
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()
W tym kodzie powinieneś wywołać stop()
wątek, gdy chcesz go zakończyć, i poczekać, aż wątek poprawnie się zakończy join()
. Wątek powinien regularnie sprawdzać flagę stop.
Są jednak przypadki, kiedy naprawdę musisz zabić wątek. Przykładem jest owijanie biblioteki zewnętrznej, która jest zajęta długimi połączeniami i chcesz ją przerwać.
Poniższy kod pozwala (z pewnymi ograniczeniami) na zgłoszenie wyjątku w wątku 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 )
(Na podstawie Killable Threads autorstwa Tomera Filiby. Cytat o wartości zwracanej przez PyThreadState_SetAsyncExc
wydaje się, że pochodzi ze starej wersji Pythona .)
Jak zauważono w dokumentacji, nie jest to magiczna kula, ponieważ jeśli wątek jest zajęty poza interpreterem Pythona, nie złapie przerwy.
Dobrym wzorcem użycia tego kodu jest, aby wątek przechwycił określony wyjątek i wykonał czyszczenie. W ten sposób możesz przerwać zadanie i nadal mieć właściwe czyszczenie.
SO_REUSEADDR
opcji gniazda, aby uniknąć Address already in use
błędu.
None
zamiast 0
w res != 1
przypadku i musiałem to wywołać ctypes.c_long(tid)
i przekazać do dowolnej funkcji ctyp, a nie bezpośrednio do TID.
Nie ma do tego oficjalnego API, nie.
Musisz użyć interfejsu API platformy, aby zabić wątek, np. Pthread_kill lub TerminateThread. Możesz uzyskać dostęp do takiego API np. Przez pythonwin lub poprzez ctypy.
Zauważ, że jest to z natury niebezpieczne. Prawdopodobnie doprowadzi to do nieściągalnego śmieci (z lokalnych zmiennych ramek stosu, które stają się śmieciami) i może doprowadzić do impasu, jeśli zabity wątek ma GIL w momencie, w którym został zabity.
multiprocessing.Process
puszkap.terminate()
W przypadkach, w których chcę zabić wątek, ale nie chcę używać flag / blokad / sygnałów / semaforów / zdarzeń / czegokolwiek, promuję wątki do pełnych procesów. W przypadku kodu, który wykorzystuje tylko kilka wątków, narzut nie jest taki zły.
Np. Przydaje się to do łatwego kończenia „wątków” pomocnika, które wykonują blokujące We / Wy
Konwersja jest trywialny: W powiązanym kodzie zastąpić wszystko threading.Thread
z multiprocessing.Process
a wszystko queue.Queue
z multiprocessing.Queue
i dodać wymagane nawoływania p.terminate()
do procesu macierzystego, który chce zabić swoje dzieckop
multiprocessing
jest fajny, ale należy pamiętać, że argumenty są marynowane w nowym procesie. Więc jeśli jeden z argumentów jest niemożliwy do zaakceptowania (jak a logging.log
), może nie być dobrym pomysłem multiprocessing
.
multiprocessing
argumenty są marynowane w nowym procesie w systemie Windows, ale Linux używa forking do ich skopiowania (Python 3.7, nie wiadomo, jakie inne wersje). Skończysz z kodem, który działa w systemie Linux, ale podnosi błędy w systemie Windows.
multiprocessing
logowanie jest trudnym biznesem. Musisz użyć QueueHandler
(zobacz ten samouczek ). Nauczyłem się tego na własnej skórze.
Jeśli próbujesz zakończyć cały program, możesz ustawić wątek jako „demon”. zobacz Thread.daemon
Jak wspomnieli inni, normą jest ustawienie flagi zatrzymania. W przypadku czegoś lekkiego (bez podklasy wątku, bez zmiennej globalnej) opcja zwrotna lambda jest opcją. (Uwaga w nawiasach 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()
Wymiana print()
z pr()
funkcji, która zawsze opróżnia ( sys.stdout.flush()
) może poprawić dokładność wyjścia powłoki.
(Testowane tylko w systemie Windows / Eclipse / Python3.3)
pr()
funkcja?
Opiera się to na wątku 2 - wątki możliwe do wywołania (przepis na Python)
Musisz wywołać PyThreadState_SetasyncExc (), która jest dostępna tylko za pośrednictwem ctypów.
Zostało to przetestowane tylko w Pythonie 2.7.3, ale prawdopodobnie będzie działać z innymi najnowszymi wydaniami 2.x.
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")
KeyboardInterrupt
aby miały szansę oczyścić. Jeśli nadal wiszą po tym, to SystemExit
jest odpowiednie, lub po prostu zabij proces z terminala.
pthread_cleanup_push()/_pop()
poprawnej implementacji wymagałoby dużo pracy i znacznie spowolniłoby interpretera.
Nigdy nie powinieneś siłą zabijać nici bez współpracy z nim.
Zabicie wątku usuwa wszelkie gwarancje, które próbują / ostatecznie blokują skonfigurowane, więc możesz zostawić blokady zamknięte, pliki otwarte itp.
Jedyny raz, kiedy można argumentować, że przymusowe zabijanie wątków jest dobrym pomysłem, to szybkie zabicie programu, ale nigdy pojedynczych wątków.
W Pythonie po prostu nie można zabić wątku bezpośrednio.
Jeśli tak naprawdę NIE potrzebujesz mieć wątku (!), Możesz zamiast pakietu wątków skorzystać z pakietu wieloprocesowego . Tutaj, aby zabić proces, możesz po prostu wywołać metodę:
yourProcess.terminate() # kill the process!
Python zabije Twój proces (w systemie Unix za pośrednictwem sygnału SIGTERM, a w systemie Windows za pośrednictwem TerminateProcess()
wywołania). Zachowaj ostrożność podczas korzystania z kolejki lub potoku! (może to uszkodzić dane w kolejce / potoku)
Zauważ, że multiprocessing.Event
i multiprocessing.Semaphore
działają dokładnie w taki sam sposób jak threading.Event
i threading.Semaphore
odpowiednio. W rzeczywistości pierwsze są klonami latters.
Jeśli NAPRAWDĘ potrzebujesz użyć Wątku, nie ma sposobu, aby go zabić bezpośrednio. Możesz jednak użyć „wątku demona” . W rzeczywistości w Pythonie wątek może zostać oflagowany jako demon :
yourThread.daemon = True # set the Thread as a "daemon thread"
Program główny zakończy działanie, gdy nie pozostaną żadne żywe wątki inne niż demony. Innymi słowy, gdy główny wątek (który jest oczywiście wątkiem innym niż demon) zakończy działanie, program zakończy działanie, nawet jeśli nadal działają pewne wątki demona.
Zauważ, że konieczne jest ustawienie wątku, tak jak daemon
przed start()
wywołaniem metody!
Oczywiście możesz i powinieneś używać daemon
nawet z multiprocessing
. Tutaj, gdy główny proces kończy działanie, próbuje zakończyć wszystkie swoje demoniczne procesy potomne.
Wreszcie, proszę zauważyć, że sys.exit()
i os.kill()
nie są wyborami.
Możesz zabić wątek, instalując śledzenie w wątku, który opuści wątek. Zobacz załączony link dla jednej możliwej implementacji.
Jeśli są jawne wywołanie time.sleep()
jako część gwintu (słownie odpytywania niektórych usług zewnętrznych), poprawa na metodzie Phillipe jest wykorzystanie limitu czasu w event
„s wait()
metody Gdziekolwieksleep()
Na przykład:
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()
Następnie, aby go uruchomić
t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread
Zaletą używania wait()
zamiast sleep()
ing i regularnego sprawdzania zdarzenia jest to, że możesz programować w dłuższych odstępach czasu, wątek jest zatrzymywany prawie natychmiast (gdy byś nie był sleep()
), a moim zdaniem kod obsługi wyjścia jest znacznie prostszy .
Zdecydowanie możliwe jest zaimplementowanie Thread.stop
metody, jak pokazano w poniższym przykładowym kodzie:
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()
Wydaje się, że Thread3
klasa uruchamia kod o około 33% szybciej niż Thread2
klasa.
self.__stop
istnienia w wątku. Zauważ, że podobnie jak większość innych rozwiązań tutaj, tak naprawdę nie zakłóci wywołania blokującego, ponieważ funkcja śledzenia jest wywoływana tylko po wprowadzeniu nowego zasięgu lokalnego. Warto również zauważyć, że sys.settrace
tak naprawdę jest przeznaczony do wdrażania debuggerów, profili itp. I jako taki jest uważany za szczegół implementacji CPython i nie ma gwarancji, że będzie istniał w innych implementacjach Pythona.
Thread2
klasą jest to, że uruchamia kod około dziesięć razy wolniej. Niektórzy ludzie mogą nadal uznać to za dopuszczalne.
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t jest twoim Thread
przedmiotem.
Przeczytaj źródło Pythona ( Modules/threadmodule.c
i Python/thread_pthread.h
) możesz zobaczyć, że Thread.ident
jest to pthread_t
typ, więc możesz zrobić wszystko, co pthread
można zrobić w użyciu Pythona libpthread
.
Aby zabić wątek, można zastosować następujące obejście:
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
Można tego użyć nawet do zakończenia wątków, których kod jest zapisany w innym module z głównego wątku. Możemy zadeklarować zmienną globalną w tym module i użyć jej do zakończenia wątku / wątków spawnowanych w tym module.
Zwykle używam tego, aby zakończyć wszystkie wątki przy wyjściu z programu. To może nie być idealny sposób na zakończenie wątku / wątków, ale może pomóc.
Spóźniłem się do tej gry, ale zmagam się z podobnym pytaniem, a poniższe wydaje się, że oba rozwiązują problem idealnie dla mnie ORAZ pozwalają mi na sprawdzenie podstawowego stanu wątku i oczyszczenie go po wyjściu demonizowanego wątku:
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()
Wydajność:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
SystemExit
na after_timeout
wątku nic do głównego wątku (który jest po prostu czeka na tego pierwszego zjazdu w tym przykładzie) zrobić?
SystemExit
ma tylko dwie specjalne właściwości: nie generuje śledzenia (gdy dowolny wątek wychodzi przez rzucenie jednego), a jeśli główny wątek wychodzi przez rzucenie jednego, ustawia status wyjścia (niemniej czekając na inne wątki niebędące demonami do wyjścia).
Jedną rzeczą, którą chcę dodać, jest to, że jeśli czytasz oficjalną dokumentację w wątku lib Python , zaleca się unikanie używania wątków „demonicznych”, gdy nie chcesz, aby wątki nagle się kończyły, z flagą, o której wspomniał Paolo Rovelli .
Z oficjalnej dokumentacji:
Wątki demonów są nagle zatrzymywane przy wyłączaniu. Ich zasoby (takie jak otwarte pliki, transakcje w bazie danych itp.) Mogą nie zostać poprawnie zwolnione. Jeśli chcesz, aby twoje wątki zatrzymały się z gracją, uczyń je nie-demonicznymi i użyj odpowiedniego mechanizmu sygnalizacyjnego, takiego jak Zdarzenie.
Myślę, że tworzenie demonicznych wątków zależy od twojej aplikacji, ale ogólnie (i moim zdaniem) lepiej unikać ich zabijania lub demonizacji. W trybie wieloprocesowym możesz użyć is_alive()
do sprawdzenia statusu procesu i „zakończyć”, aby je zakończyć (unikasz również problemów GIL). Ale czasem można znaleźć więcej problemów podczas wykonywania kodu w systemie Windows.
I zawsze pamiętaj, że jeśli masz „aktywne wątki”, interpreter Pythona będzie działał, aby na nie poczekać. (Z tego powodu demon może ci pomóc, jeśli nie ma znaczenia, nagle się kończy).
W tym celu zbudowano bibliotekę stopit . Chociaż niektóre z tych samych ostrzeżeń wymienionych tutaj nadal mają zastosowanie, przynajmniej ta biblioteka przedstawia regularną, powtarzalną technikę osiągania określonego celu.
Chociaż jest dość stary, może to być przydatne rozwiązanie dla niektórych:
Mały moduł rozszerzający funkcjonalność modułu wątków - pozwala jednemu wątkowi zgłaszać wyjątki w kontekście innego wątku. Podnosząc
SystemExit
, możesz w końcu zabić wątki pytona.
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)
Pozwala to „wątkowi zgłosić wyjątki w kontekście innego wątku” i w ten sposób zakończony wątek może obsłużyć zakończenie bez regularnego sprawdzania flagi przerwania.
Jednak zgodnie z jego oryginalnym źródłem istnieją pewne problemy z tym kodem.
- Wyjątek zostanie zgłoszony tylko podczas wykonywania kodu bajtowego Pythona. Jeśli wątek wywołuje rodzimą / wbudowaną funkcję blokującą, wyjątek zostanie zgłoszony tylko wtedy, gdy wykonanie powróci do kodu python.
- Istnieje również problem, jeśli wbudowana funkcja wewnętrznie wywołuje PyErr_Clear (), co skutecznie anulowałoby oczekujący wyjątek. Możesz spróbować go podnieść ponownie.
- Tylko typy wyjątków można bezpiecznie zgłaszać. Wystąpienia wyjątków mogą powodować nieoczekiwane zachowanie, a zatem są ograniczone.
- Na przykład: t1.raise_exc (TypeError), a nie t1.raise_exc (TypeError („blah”)).
- IMHO to błąd, który zgłosiłem jako jeden. Aby uzyskać więcej informacji, http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- Poprosiłem o ujawnienie tej funkcji we wbudowanym module wątku, ale ponieważ ctypy stały się standardową biblioteką (od wersji 2.5), a ta
funkcja prawdopodobnie nie będzie niezależna od implementacji, może pozostać
niewidoczna.
Pieter Hintjens - jeden z założycieli projektu ØMQ - mówi, że używanie ØMQ i unikanie prymitywów synchronizacji, takich jak zamki, muteksy, zdarzenia itp., Jest najbezpieczniejszym i najbezpieczniejszym sposobem pisania programów wielowątkowych:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
Obejmuje to powiedzenie wątkowi potomnemu, że powinien anulować swoją pracę. Można to zrobić, wyposażając wątek w gniazdo ØMQ i odpytując w tym gnieździe w celu wyświetlenia komunikatu z informacją, że należy go anulować.
Link zawiera także przykład wielowątkowego kodu Pythona z ØMQ.
Zakładając, że chcesz mieć wiele wątków tej samej funkcji, jest to IMHO najłatwiejsza implementacja, aby zatrzymać jeden przez 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
Zaletą jest to, że możesz mieć wiele takich samych i różnych funkcji i zatrzymać je wszystkie functionname.stop
Jeśli chcesz mieć tylko jeden wątek funkcji, nie musisz pamiętać identyfikatora. Przestań, jeśli doit.stop
> 0.
Aby rozwinąć pomysł @ SCB (dokładnie tego potrzebowałem), aby utworzyć podklasę KillableThread z dostosowaną funkcją:
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()
Oczywiście, podobnie jak w przypadku @SBC, wątek nie czeka na uruchomienie nowej pętli w celu zatrzymania. W tym przykładzie zobaczysz komunikat „Killing Thread” wydrukowany zaraz po „About to kill thread” zamiast czekać jeszcze 4 sekundy na zakończenie wątku (ponieważ spaliśmy już 6 sekund).
Drugim argumentem konstruktora KillableThread jest twoja funkcja niestandardowa (tutaj print_msg). Argument Argumenty to argumenty, które będą używane podczas wywoływania funkcji ((„witaj świecie”)) tutaj.
Jak wspomniano w @ Kozyarchuk za odpowiedź , instalowanie ślad działa. Ponieważ ta odpowiedź nie zawierała kodu, oto działający, gotowy do użycia przykład:
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
Zatrzymuje się po wydrukowaniu 1
i 2
. 3
nie jest drukowany.
Możesz wykonać polecenie w procesie, a następnie zabić je przy użyciu identyfikatora procesu. Musiałem zsynchronizować dwa wątki, z których jeden sam się nie zwraca.
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;
Rozpocznij wątek podrzędny za pomocą 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
To zła odpowiedź, zobacz komentarze
Oto jak to zrobić:
from threading import *
...
for thread in enumerate():
if thread.isAlive():
try:
thread._Thread__stop()
except:
print(str(thread.getName()) + ' could not be terminated'))
Daj mu kilka sekund, a następnie twój wątek powinien zostać zatrzymany. Sprawdź także thread._Thread__delete()
metodę.
Polecam thread.quit()
metodę dla wygody. Na przykład, jeśli masz gniazdo w swoim wątku, zaleciłbym utworzenie quit()
metody w klasie gniazda-uchwyt, zakończenie gniazda, a następnie uruchomienie thread._Thread__stop()
wewnątrz quit()
.
_Thread__stop()
jedynie zaznacza wątek jako zatrzymany , tak naprawdę nie zatrzymuje wątku! Nigdy tego nie rób. Przeczytaj .
Jeśli naprawdę potrzebujesz możliwości zabicia podzadania, użyj alternatywnej implementacji. multiprocessing
i gevent
oba wspierają bezkrytycznie zabijanie „wątku”.
Wątek Pythona nie obsługuje anulowania. Nawet nie próbuj. Twój kod najprawdopodobniej zakleszczy się, uszkodzi lub wyciek pamięci lub wywoła inne niezamierzone „interesujące” trudne do debugowania efekty, które zdarzają się rzadko i nie są deterministyczne.