Как правило, это плохой шаблон, чтобы убить поток внезапно, на Python и на любом языке. Подумайте о следующих случаях:
Хороший способ справиться с этим, если вы можете себе это позволить (если вы управляете своими потоками), должен иметь флаг exit_request, который каждый поток проверяет на регулярном интервале, чтобы узнать, чтобы выйти.
Например:
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):
super(StoppableThread, self).__init__()
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
В этом коде вы должны вызвать stop () в потоке, когда хотите, чтобы он вышел, и дождитесь, пока поток выйдите правильно, используя join (). Поток должен проверять флаг остановки через регулярные промежутки времени.
Однако есть случаи, когда вам действительно нужно убить поток. Например, когда вы завершаете внешнюю библиотеку, занятую для длинных вызовов, и вы хотите ее прервать.
Следующий код позволяет (с некоторыми ограничениями) поднимать исключение в потоке 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 )
(На основе Killable Threads от Tomer Filiba. Цитата о возвратном значении PyThreadState_SetAsyncExc
, по-видимому, принадлежит старой версии Python .)
Как отмечено в документации, это не волшебная пуля, потому что если поток занят вне интерпретатора Python, он не поймает прерывание.
Хорошая схема использования этого кода, чтобы поток зависел от конкретного исключения и выполнял очистку. Таким образом, вы можете прервать задачу и по-прежнему иметь правильную очистку.