Is there any way to kill a Thread?
是否可以在不设置/检查任何标志/信号量的情况下终止运行线程?
在Python和任何语言中,突然断线都是一种糟糕的模式。想想下面的例子:
- 线程持有一个必须正确关闭的关键资源。
- 该线程还创建了其他几个必须终止的线程。
如果您能负担得起(如果您管理自己的线程),那么处理这个问题的好方法是设置一个exit-request标志,每个线程定期检查该标志,看看是否该退出。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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线程中引发异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | 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 ) |
(基于Tomer Filiba的可杀线。关于
如文档中所述,这不是一个神奇的子弹,因为如果线程在Python解释器之外很忙,它就不会捕获中断。
此代码的一个好的使用模式是让线程捕获特定的异常并执行清理。这样,您就可以中断一个任务,并且仍然可以进行适当的清理。
没有官方的API可以做到这一点。
您需要使用平台API来终止线程,例如pthread_kill或terminateThread。您可以通过pythonwin或ctypes访问此类API。
注意这是固有的不安全因素。如果被杀死的线程在被杀死时拥有gil,那么它很可能会导致无法回收的垃圾(来自变成垃圾的堆栈帧的局部变量),并可能导致死锁。
一个
在我想杀死一个线程,但不想使用标志/locks/signals/signals/events/whatever的情况下,我会将线程升级为完整的进程。对于只使用几个线程的代码,开销并没有那么糟糕。
例如,这很容易终止执行阻塞I/O的助手"线程"。
转换很简单:在相关代码中,将所有
Python博士
如果您试图终止整个程序,可以将线程设置为"守护进程"。看见线程守护进程
这是基于thread2——可杀死线程(python配方)
您需要调用pythreadstate_setAsyncExc(),它只能通过CTypes使用。
这只在Python2.7.3上进行了测试,但它很可能与最近的其他2.x版本一起使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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") |
如果不配合线程,则不应强制终止线程。
终止一个线程会删除Try/Finally块设置的任何保证,从而使锁保持锁定、文件保持打开状态等。
唯一一次你可以争辩说强制杀死线程是个好主意,那就是快速杀死一个程序,但不要杀死单个线程。
正如其他人提到的,规范是设置一个停止标志。对于轻量级的东西(没有线程的子类化,没有全局变量),lambda回调是一个选项。(注意
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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() |
用总是刷新(
(仅在windows/eclipse/python3.3上测试)
在Python中,不能直接杀死线程。
如果你真的不需要线程!!),您可以使用多处理包。在这里,要杀死一个进程,只需调用方法:
1 | yourProcess.terminate() # kill the process! |
python将终止您的进程(在Unix上通过sigterm信号,而在Windows上通过
注意,
如果你真的需要使用线程,就没有办法直接杀死它。但是,您可以使用"守护进程线程"。实际上,在python中,线程可以标记为守护进程:
1 | yourThread.daemon = True # set the Thread as a"daemon thread" |
当没有活动的非守护进程线程时,主程序将退出。换句话说,当主线程(当然是非守护进程线程)完成其操作时,即使仍有一些守护进程线程在工作,程序也将退出。
注意,在调用
当然,您可以也应该使用
最后,请注意,
通过将跟踪安装到将退出线程的线程中,可以杀死线程。请参阅附件一个可能的实现。
用Python杀死线程
如果你不剪断一根线就更好了。一种方法是在线程的循环中引入一个"try"块,并在您想要停止线程时抛出一个异常(例如break/return/…)。这会使您的/while/…)。我在我的应用程序上使用过这个,它可以工作…
如下面的示例代码所示,绝对可以实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | 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() |
1 2 3 | from ctypes import * pthread = cdll.LoadLibrary("libpthread-2.15.so") pthread.pthread_cancel(c_ulong(t.ident)) |
t是你的
阅读python源代码(
以下解决方法可用于终止线程:
1 2 3 4 5 6 7 8 9 10 11 | kill_threads = False def doSomething(): global kill_threads while True: if kill_threads: thread.exit() ...... ...... thread.start_new_thread(doSomething, ()) |
这甚至可以用于终止线程,这些线程的代码是从主线程写入另一个模块中的。我们可以在该模块中声明一个全局变量,并使用它终止在该模块中生成的线程。
我通常使用它在程序退出时终止所有线程。这可能不是终止线程的完美方法,但可能会有所帮助。
我想补充的一点是,如果您在线程lib python中阅读官方文档,建议您在不希望线程突然结束时,避免使用"恶魔"线程,并使用paolo rovelli提到的标志。
官方文件:
Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signaling mechanism such as an Event.
我认为创建daemonic线程取决于您的应用程序,但总的来说(在我看来),最好避免杀死它们或使它们成为daemonic。在多处理中,您可以使用
并且始终记住,如果您有"活动线程",那么python解释器将运行等待它们。(因为这个恶魔可以帮助你,如果不重要突然结束)。
我已经很晚了,但我一直在努力解决类似的问题,下面的内容似乎都能很好地解决这个问题,并让我在守护进程的子线程退出时进行一些基本的线程状态检查和清理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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() |
产量:
1 2 3 4 | 0 1 KILL MAIN THREAD: 140013208254208 'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568] |
如果您显式地调用
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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() |
然后运行它
1 2 3 4 5 6 | t = KillableThread(sleep_interval=5) t.start() # Every 5 seconds it prints: #: Do Something t.kill() #: Killing Thread |
使用
有一个为此目的而建的图书馆,别说了。尽管这里列出的一些相同的警告仍然适用,但至少这个库提供了一种常规的、可重复的技术来实现所述的目标。
这似乎适用于Windows7上的pywin32。
1 2 3 | my_thread = threading.Thread() my_thread.start() my_thread._Thread__stop() |
This is a bad answer, see the comments
方法如下:
1 2 3 4 5 6 7 8 9 10 | from threading import * ... for thread in enumerate(): if thread.isAlive(): try: thread._Thread__stop() except: print(str(thread.getName()) + ' could not be terminated')) |
给它几秒钟,然后你的线程应该停止。同时检查
为了方便起见,我建议采用
用setdaemon(true)启动子线程。
1 2 3 4 5 6 7 8 9 10 11 | 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 |
虽然它相当古老,但对于一些人来说,这可能是一个方便的解决方案:
A little module that extends the threading's module functionality --
allows one thread to raise exceptions in the context of another
thread. By raisingSystemExit , you can finally kill python threads.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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) |
因此,它允许"线程在另一个线程的上下文中引发异常",这样,终止的线程就可以处理终止,而无需定期检查中止标志。
但是,根据其原始源代码,此代码存在一些问题。
- The exception will be raised only when executing python bytecode. If your thread calls a native/built-in blocking function, the
exception will be raised only when execution returns to the python
code.
- There is also an issue if the built-in function internally calls PyErr_Clear(), which would effectively cancel your pending exception.
You can try to raise it again.- Only exception types can be raised safely. Exception instances are likely to cause unexpected behavior, and are thus restricted.
- For example: t1.raise_exc(TypeError) and not t1.raise_exc(TypeError("blah")).
- IMHO it's a bug, and I reported it as one. For more info, http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- I asked to expose this function in the built-in thread module, but since ctypes has become a standard library (as of 2.5), and this
feature is not likely to be implementation-agnostic, it may be kept
unexposed.
Pieter Hintjens——的创始人之一?MQ项目——说,使用?MQ和避免同步原语(如锁、互斥锁、事件等)是编写多线程程序最明智和最安全的方法:
http://zguide.zeromq.org/py:all使用zeromq进行多线程处理
这包括告诉子线程,它应该取消它的工作。这可以通过在螺纹上安装一个?MQ套接字,并在该套接字上轮询一条消息,表明它应该取消。
该链接还提供了一个关于多线程python代码的示例。MQ。
可以在进程中执行命令,然后使用进程ID将其杀死。我需要在两个线程之间同步,其中一个线程本身不会返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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; |
如果您真的需要杀死子任务的能力,请使用备用实现。
Python的线程不支持取消。甚至不要尝试。您的代码很可能死锁、损坏或泄漏内存,或者具有其他意想不到的难以调试的"有趣"效果,这些效果很少发生,而且不具有确定性。