Answers:
用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()
。线程应定期检查停止标志。
但是,在某些情况下,您确实需要杀死线程。一个示例是当您包装一个忙于长时间调用的外部库并且想要中断它时。
以下代码允许(有一些限制)在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(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的Killable Threads。有关return值的引用PyThreadState_SetAsyncExc
似乎来自旧版本的Python。)
如文档中所述,这不是灵丹妙药,因为如果线程在Python解释器之外很忙,它将无法捕获中断。
此代码的一个很好的用法模式是让线程捕获特定的异常并执行清理。这样,您可以中断任务并仍然进行适当的清理。
SO_REUSEADDR
套接字选项来避免Address already in use
错误。
None
而不是0
针对这种res != 1
情况,并且我必须调用ctypes.c_long(tid)
并将其传递给任何ctypes函数,而不是直接传递给tid。
没有官方的API可以这样做。
您需要使用平台API杀死线程,例如pthread_kill或TerminateThread。您可以通过pythonwin或ctypes访问此类API。
请注意,这本质上是不安全的。如果被杀死的线程在被杀死时具有GIL,则可能会导致无法收集的垃圾(来自成为垃圾的堆栈帧的局部变量),并可能导致死锁。
一个multiprocessing.Process
罐头p.terminate()
在我想杀死一个线程,但又不想使用标志/锁/信号/信号量/事件/任何东西的情况下,我会将线程提升为完整进程。对于仅使用几个线程的代码,开销并不是那么糟糕。
例如,这样做很方便,可以轻松终止执行阻塞I / O的助手“线程”
转换是微不足道的:在相关代码中,将全部替换threading.Thread
为multiprocessing.Process
和全部替换queue.Queue
为multiprocessing.Queue
,并将所需的调用添加p.terminate()
到要杀死其子级的父进程中p
multiprocessing
很好,但请注意,将参数腌制到新过程中。因此,如果其中一个参数是不可选择的东西(如a logging.log
),则使用它可能不是一个好主意multiprocessing
。
multiprocessing
参数在Windows上被添加到新进程中,但是Linux使用分叉来复制它们(Python 3.7,不确定其他版本)。因此,您将获得在Linux上可以使用的代码,但在Windows上却出现了pickle错误。
如果试图终止整个程序,则可以将线程设置为“守护程序”。参见 Thread.daemon
正如其他人提到的那样,规范是设置停止标志。对于轻量级的东西(没有Thread的子类,没有全局变量),可以选择使用lambda回调。(请注意中的括号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()
替换print()
为pr()
始终刷新(sys.stdout.flush()
)的函数可以提高Shell输出的精度。
(仅在Windows / Eclipse / Python3.3上测试过)
pr()
功能?
您需要调用PyThreadState_SetasyncExc(),该方法仅可通过ctypes使用。
该功能仅在Python 2.7.3上进行了测试,但可能与其他最新的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
以便它们有机会清理。如果它们在此之后仍然挂起,则SystemExit
是适当的,或者只是从终端中终止该进程。
pthread_cleanup_push()/_pop()
,但要正确实现,将需要进行大量工作,这将大大降低解释器的速度。
如果不配合使用线程,切勿强行杀死它。
杀死线程会删除所有尝试/最终阻止设置的保证,因此您可能会锁定锁,打开文件等。
唯一可以断言强行杀死线程是个好主意的方法是快速杀死程序,但绝不要杀死单个线程。
在Python中,您根本无法直接杀死线程。
如果您实际上并不需要Thread(!),则可以使用 multiprocessing软件包,而不是使用threading软件包来做。在这里,要杀死一个进程,您可以简单地调用方法:
yourProcess.terminate() # kill the process!
Python将杀死您的进程(在Unix上通过SIGTERM信号,而在Windows上通过TerminateProcess()
调用)。在使用队列或管道时,请注意使用它!(它可能会损坏队列/管道中的数据)
请注意,multiprocessing.Event
和的multiprocessing.Semaphore
工作方式与threading.Event
和的完全相同threading.Semaphore
。实际上,第一个是后者的克隆。
如果您确实需要使用线程,则无法直接杀死它。但是,您可以使用“守护程序线程”。实际上,在Python中,可以将Thread标记为守护程序:
yourThread.daemon = True # set the Thread as a "daemon thread"
当没有活动的非守护线程时,主程序将退出。换句话说,当您的主线程(当然,这是一个非守护程序线程)完成其操作时,即使仍有一些守护程序线程在工作,程序也会退出。
请注意,有必要daemon
在start()
调用该方法之前设置一个线程!
当然,daemon
甚至可以使用multiprocessing
。在这里,当主进程退出时,它将尝试终止其所有守护进程。
最后,请注意,sys.exit()
而os.kill()
不是选择。
您可以通过在将退出线程的线程中安装跟踪来杀死线程。请参阅附件链接以获取一种可能的实现。
如果你是显式调用time.sleep()
为你的线程(比如查询一些外部的服务)的一部分,在菲利普的方法的改进是使用了超时的event
的wait()
方法,无论你sleep()
例如:
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()
然后运行
t = KillableThread(sleep_interval=5)
t.start()
# Every 5 seconds it prints:
#: Do Something
t.kill()
#: Killing Thread
使用wait()
而不是sleep()
ing并定期检查事件的优点是您可以在更长的睡眠间隔内进行编程,线程几乎立即停止(原本应该是sleep()
ing),并且我认为处理退出的代码明显更简单。
绝对有可能实现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
类似乎比快大约33%的运行代码Thread2
类。
self.__stop
设置为线程的聪明方法。请注意,与此处的大多数其他解决方案一样,它实际上不会中断阻塞调用,因为仅当输入新的本地作用域时才调用trace函数。同样值得注意的是,它sys.settrace
确实意味着要实现调试器,配置文件等,因此被视为CPython的实现细节,并且不能保证在其他Python实现中也存在。
Thread2
该类的最大问题之一是它运行的代码慢了大约十倍。有些人可能仍然认为这可以接受。
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
类型,因此您可以pthread
在python use中做任何事情libpthread
。
可以使用以下解决方法杀死线程:
kill_threads = False
def doSomething():
global kill_threads
while True:
if kill_threads:
thread.exit()
......
......
thread.start_new_thread(doSomething, ())
这甚至可以用于终止从主线程终止其代码在另一个模块中编写的线程。我们可以在该模块中声明一个全局变量,并使用它终止该模块中产生的线程。
我通常使用它在程序出口处终止所有线程。这可能不是终止线程的理想方法,但可能会有所帮助。
我玩这个游戏很晚,但是我一直在努力解决类似的问题,以下内容似乎可以为我很好地解决问题,并且让守护子线程退出时让我做一些基本的线程状态检查和清理工作:
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()
产量:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
SystemExit
在after_timeout
线程上引发对主线程有任何作用(在此示例中,主线程只是在等待前者退出)?
SystemExit
仅具有两个特殊属性:它不产生回溯(当任何线程抛出一个异常退出时),并且如果主线程抛出一个异常退出,则它设置退出状态(尽管如此,它仍在等待其他非守护进程线程)退出)。
我想补充的一件事是,如果您在线程lib Python中阅读了官方文档,建议您避免使用“恶魔”线程,如果您不希望线程突然结束,请使用Paolo Rovelli 提到的标志。
根据官方文档:
守护程序线程在关闭时突然停止。它们的资源(例如打开的文件,数据库事务等)可能无法正确释放。如果您希望线程正常停止,请将它们设置为非守护进程,并使用适当的信令机制(例如事件)。
我认为创建守护线程取决于您的应用程序,但总的来说(我认为)最好避免杀死它们或使其成为守护线程。在多处理中,您可以is_alive()
用来检查过程状态并“终止”以完成过程(还可以避免GIL问题)。但是,有时在Windows中执行代码时会发现更多问题。
永远记住,如果您有“活动线程”,Python解释器将运行以等待它们。(因为这个守护进程可以帮助您,如果没关系突然结束)。
虽然它已经很老了,但对于某些人来说可能是一个方便的解决方案:
一个扩展了线程的模块功能的小模块-允许一个线程在另一个线程的上下文中引发异常。通过提高
SystemExit
,您最终可以杀死python线程。
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)
因此,它允许“线程在另一个线程的上下文中引发异常”,这样,终止的线程可以处理终止,而无需定期检查中止标志。
但是,根据其原始来源,此代码存在一些问题。
- 仅在执行python字节码时才会引发异常。如果您的线程调用了本机/内置阻止函数,则仅当执行返回到python代码时才会引发异常。
- 如果内置函数在内部调用PyErr_Clear()也会存在一个问题,这将有效地取消您的未决异常。您可以尝试再次提高它。
- 只有异常类型可以安全地引发。异常实例可能会导致意外行为,因此受到限制。
- 例如:t1.raise_exc(TypeError)而不是t1.raise_exc(TypeError(“ blah”))。
- 恕我直言,这是一个错误,我将其报告为一个。有关更多信息,请访问 http://mail.python.org/pipermail/python-dev/2006-August/068158.html
- 我要求在内置线程模块中公开此功能,但是由于ctypes已成为标准库(从2.5版开始),并且此
功能不太可能与实现无关,因此可以不公开
。
ØMQ项目的创始人之一Pieter Hintjens 说,使用ØMQ并避免使用同步原语(例如锁,互斥体,事件等),是编写多线程程序的最安全的方法:
http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ
这包括告诉子线程应该取消其工作。这可以通过为该线程配备一个ØMQ套接字并在该套接字上轮询以获取一条消息指出该线程应取消的信息来完成。
该链接还提供了有关带有ØMQ的多线程python代码的示例。
假设您要具有多个具有相同功能的线程,这是恕我直言,最简单的实现是通过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
不错的是,您可以拥有多个相同和不同的功能,并通过以下方式将其全部停止 functionname.stop
如果您只想使用该函数的一个线程,则无需记住ID。如果doit.stop
> 0 ,则停止。
只是基于@SCB的想法(这正是我所需要的)来创建具有自定义函数的KillableThread子类:
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()
自然,就像@SBC一样,线程不必等待运行新循环来停止。在此示例中,您将在“即将杀死线程”之后看到“杀死线程”消息,而不是再等待4秒钟才能完成线程(因为我们已经睡了6秒钟)。
KillableThread构造函数中的第二个参数是您的自定义函数(此处为print_msg)。Args参数是在此处调用函数((“ hello world”))时将使用的参数。
如@Kozyarchuk的答案中所述,安装跟踪有效。由于此答案不包含任何代码,因此下面是一个可以使用的示例:
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
打印1
并打印后停止2
。3
不打印。
您可以在进程中执行命令,然后使用进程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;
使用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
这是一个错误的答案,请参阅评论
方法如下:
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()
方法。
thread.quit()
为了方便起见,我建议使用一种方法。例如,如果您的线程中有一个套接字,建议quit()
您在套接字句柄类中创建一个方法,终止该套接字,然后在的thread._Thread__stop()
内部运行quit()
。
_Thread__stop()
只是将一个线程标记为已停止,实际上并没有停止该线程!永远不要这样做。阅读。
如果您确实需要杀死子任务的能力,请使用替代实现。multiprocessing
并且gevent
都支持滥杀“线程”。
Python的线程不支持取消。想都别想。您的代码很可能会死锁,损坏或泄漏内存,或者具有其他意想不到的“有趣”难以调试的效果,这种情况很少且不确定地发生。