有没有什么办法杀死Python中的线程?
是否有可能终止一个正在运行的线程没有设置/检查任何标志/信号/等?
在Python和任何语言中,突然杀死一个线程通常是一个不好的模式。 考虑以下情况:
- 线程正在持有必须正确closures的关键资源
- 该线程已经创build了其他几个线程,也必须被杀死。
如果你能负担得起(如果你正在pipe理自己的线程),处理这个好方法是有一个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线程中引发exception:
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(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(tid, 0) 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 )
正如文档中指出的那样,这不是一个神奇的项目,因为如果线程在Python解释器之外忙碌,它将不会捕获中断。
此代码的一个很好的使用模式是让线程捕获一个特定的exception并执行清理。 这样,你可以中断一个任务,仍然有适当的清理。
没有官方的API来做到这一点,没有。
您需要使用平台API来终止线程,例如pthread_kill或TerminateThread。 你可以通过pythonwin或者ctypes来访问这样的API。
请注意,这本质上是不安全的。 这可能会导致无法收集的垃圾(从堆栈帧的局部variables变成垃圾),并且可能导致死锁,如果被杀死的线程在被杀死的时候具有GIL。
如果您试图终止整个程序,您可以将该线程设置为“守护进程”。 请参阅Thread.daemon
一个multiprocessing.Process
可以p.terminate()
在我想杀死一个线程但不想使用flags / locks / signals / semaphores / events / whatever的情况下,我将线程提升为完整的进程。 对于只使用几个线程的代码来说,开销并不是那么糟糕。
例如,这可以方便地终止执行阻塞I / O的助手“线程”
转换是微不足道的:在相关的代码中,用multiprocessing.Process
queue.Queue
replace所有threading.Thread
和所有queue.Queue
与multiprocessing.Queue
并添加所需的p.terminate()
调用到您的父进程,它想要杀死它的子p
Python文档
你不应该强行杀死一个线程,而不是与之合作。
杀死一个线程删除任何保证try / finally块设置,所以你可能会保持lockinglocking,打开文件等。
唯一一次你可以争论强行杀死线程是一个好主意,就是快速杀死一个程序,但从来没有单线程。
这是基于thread2 – killable线程(Python配方)
你需要调用PyThreadState_SetasyncExc(),它只能通过ctypes使用。
这只是在Python 2.7.3上进行了testing,但它可能与最近的其他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")
正如其他人所说,规范是设置一个停止标志。 对于轻量级的东西(没有Thread的子类,没有全局variables),lambdacallback是一个选项。 (注意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()
使用总是刷新的pr()
函数replaceprint()
( sys.stdout.flush()
)可能会提高shell输出的精度。
(只在Windows / Eclipse / Python3.3上testing过)
在Python中,你不能直接杀死一个线程。
如果你真的不需要一个线程(!),你可以做什么,而不是使用线程包 ,是使用多处理包 。 在这里,要杀死一个进程,你可以简单地调用这个方法:
yourProcess.terminate() # kill the process!
Python会杀死你的进程(在Unix上通过SIGTERM信号,而在Windows上通过TerminateProcess()
调用)。 使用队列或pipe道时请注意使用它! (可能会破坏队列/pipe道中的数据)
请注意, multiprocessing.Event
和multiprocessing.Semaphore
工作方式与threading.Event
和threading.Semaphore
完全相同。 事实上,第一个是后者的克隆。
如果您真的需要使用线程,则无法直接杀死它。 但是你可以做的是使用“守护线程” 。 事实上,在Python中,一个线程可以被标记为守护进程 :
yourThread.daemon = True # set the Thread as a "daemon thread"
没有活动的非守护进程线程时,主程序将退出。 换句话说,当你的主线程(当然是非守护线程)将完成其操作时,即使仍然有一些守护线程正在工作,程序也将退出。
请注意,在调用start()
方法之前,需要将Thread设置为daemon
。
当然,即使在multiprocessing
,也可以并应该使用daemon
。 在这里,当主进程退出时,它试图终止其所有的守护进程。
最后,请注意, sys.exit()
和os.kill()
不是select。
您可以通过将线程安装到将退出线程的线程来终止线程。 请参阅附加链接,了解可能的实施
在Python中杀死一个线程
如果你不杀死一个线程会更好。 一种方法可能是在线程的循环中引入一个“try”块,当你想停止线程时抛出一个exception(例如break / return / …,以停止for / while / …)。 我用我的应用程序,它的工作原理…
如下面的示例代码所示,可以实现一个Thread.stop
方法:
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()
Thread3
类似乎比Thread2
类运行速度快大约33%。
from ctypes import * pthread = cdll.LoadLibrary("libpthread-2.15.so") pthread.pthread_cancel(c_ulong(t.ident))
t是你的Thread
对象。
阅读python源代码( Modules/threadmodule.c
和Python/thread_pthread.h
),你可以看到Thread.ident
是一个pthread_t
types,所以你可以做任何pthread
可以在python中使用libpthread
。
我想补充的一件事是,如果你阅读线程库Python的官方文档,build议避免使用“恶魔”线程,当你不希望线程突然结束,与Paolo Rovelli 提到的标志。
从官方文件:
守护程序线程在关机时突然停止。 他们的资源(如打开文件,数据库事务等)可能无法正确释放。 如果你想让你的线程正常停止,使它们不是守护进程,并使用适当的信号机制,如事件。
我认为创build守护线程取决于你的应用程序,但总的来说(在我看来)最好避免杀死它们或使它们变成守护神。 在多处理中,您可以使用is_alive()
来检查进程状态,并使用“终止”来完成它们(同时避免GIL问题)。 但是,在Windows中执行代码时,您可能会发现更多的问题。
请记住,如果您有“活动的线程”,Python解释器将运行以等待它们。 (因为这个守护神可以帮助你,如果不要紧急结束)。
这是一个不好的答案,请参阅评论
以下是如何做到这一点:
from threading import * ... for thread in enumerate(): if thread.isAlive(): try: thread._Thread__stop() except: print(str(thread.getName()) + ' could not be terminated'))
给它几秒钟,然后你的线程应该停止。 检查thread._Thread__delete()
方法。
为了方便起见,我build议使用一个thread.quit()
方法。 例如,如果你的线程中有套接字,我build议在你的套接字句柄类中创build一个quit()
方法,终止套接字,然后在你的quit()
运行一个thread._Thread__stop()
quit()
。
这似乎与Windows 7上的pywin32
my_thread = threading.Thread() my_thread.start() my_thread._Thread__stop()
用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
我认为这是一个相当干净的解决scheme: http : //code.activestate.com/recipes/496960-thread2-killable-threads/
它允许“线程在另一个线程的上下文中引发exception”。 通过这种方式,终止的线程可以处理终止,而无需定期检查一个abbort标志。
如果你真的需要杀死一个子任务的能力,使用一个替代的实现。 multiprocessing
和gevent
都支持不加区分地杀死一个“线程”。
Python的线程不支持取消。 想都别想。 你的代码很可能会死锁,损坏或泄漏内存,或者有其他意想不到的“有趣的”难以debugging的效果,这种效果很less发生,也不确定。
您可以在一个进程中执行您的命令,然后使用进程ID来终止它。 我需要在两个线程之间进行同步,其中一个线程本身不会返回。
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;