是否可以在不设置/检查任何标志/信号量/等的情况下终止正在运行的线程?
在 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, *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()
在此代码中,您应该stop()在希望线程退出时调用它,并使用 . 等待线程正确退出join()。线程应定期检查停止标志。
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 an 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 exception in the context of the thread represented by this instance. """ _async_raise( self._get_my_tid(), exctype )
(基于Tomer Filiba 的Killable Threads。关于返回值的引用PyThreadState_SetAsyncExc似乎来自Python 的旧版本。)
PyThreadState_SetAsyncExc
如文档中所述,这不是灵丹妙药,因为如果线程在 Python 解释器之外忙,它就不会捕获中断。
此代码的一个良好使用模式是让线程捕获特定异常并执行清理。这样,您可以中断任务并仍然进行适当的清理。