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)
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一样,线程不必等待运行新循环就可以停止。在本例中,您将看到“killing thread”消息打印在“about to kill thread”之后,而不是再等待4秒钟线程完成(因为我们已经睡了6秒钟)。 killablethread构造函数中的第二个参数是自定义函数(此处打印消息)。args参数是在此处调用函数((“hello world”)时将使用的参数。
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()
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 )
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")
20条答案
按热度按时间6yoyoihd1#
虽然它相当古老,但对于某些人来说,这可能是一个方便的解决方案:
一个扩展线程模块功能的小模块——允许一个线程在另一个线程的上下文中引发异常。提高
SystemExit
,您最终可以杀死python线程。因此,它允许“一个线程在另一个线程的上下文中引发异常”,这样,终止的threa
4si2a6ki2#
仅仅是基于@scb的想法(这正是我所需要的)创建一个具有自定义函数的killablethread子类:
自然地,就像@sbc一样,线程不必等待运行新循环就可以停止。在本例中,您将看到“killing thread”消息打印在“about to kill thread”之后,而不是再等待4秒钟线程完成(因为我们已经睡了6秒钟)。
killablethread构造函数中的第二个参数是自定义函数(此处打印消息)。args参数是在此处调用函数((“hello world”)时将使用的参数。
pxq42qpu3#
下面是另一种方法,但是使用非常干净和简单的代码,它将在2021年在python 3.7中运行:
从这里改编:https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/
2nc8po8w4#
有一个为此目的而建的图书馆,停止吧。尽管本文列出的一些注意事项仍然适用,但至少该库提供了一种用于实现所述目标的常规、可重复的技术。
deyfvvtc5#
我玩这个游戏已经晚了很久,但我一直在努力解决一个类似的问题,下面的内容似乎可以完美地解决这个问题,并让我在守护子线程退出时执行一些基本的线程状态检查和清理:
产量:
46scxncf6#
以下变通方法可用于终止线程:
这甚至可以用于从主线程终止线程(其代码写在另一个模块中)。我们可以在该模块中声明一个全局变量,并使用它终止在该模块中生成的线程。
我通常使用它来终止程序出口处的所有线程。这可能不是终止线程的完美方式,但可能会有所帮助。
z9ju0rcb7#
这是你的
Thread
对象阅读python源代码(
Modules/threadmodule.c
及Python/thread_pthread.h
)你可以看到Thread.ident
是一个pthread_t
输入,这样你就可以做任何事情pthread
能在python中使用吗libpthread
.ecr0jaav8#
我想补充的一点是,如果您阅读线程库python中的官方文档,建议避免使用“恶魔”线程,因为您不希望线程突然结束,并带有paolo rovelli提到的标志。
从正式文件:
守护进程线程在关闭时突然停止。他们的资源(如打开的文件、数据库事务等)可能无法正常释放。如果希望线程正常停止,请将其设为非守护线程,并使用适当的信号机制,如事件。
我认为创建daemonic线程取决于您的应用程序,但通常(在我看来)最好避免杀死它们或使它们成为daemonic线程。在多处理中,您可以使用
is_alive()
检查流程状态并“终止”完成它们(也可以避免gil问题)。但有时,当您在windows中执行代码时,您会发现更多问题。请始终记住,如果您有“活动线程”,python解释器将为它们运行(因为这个恶魔可以帮你,如果不要紧的话(突然结束)。
gopyfrb39#
在python和任何语言中,突然终止线程通常都是不好的模式。请考虑以下情况:
线程持有一个必须正确关闭的关键资源
该线程还创建了其他几个必须终止的线程。
如果您负担得起(如果您正在管理自己的线程),处理这个问题的好方法是使用一个exit_请求标志,每个线程定期检查该标志,以查看是否是退出的时间。
例如:
在这段代码中,您应该调用
stop()
当您希望线程退出时,在线程上单击,然后使用join()
. 线程应定期检查停止标志。但是,在某些情况下,确实需要终止线程。例如,当您正在 Package 一个因长时间呼叫而忙碌的外部库时,您想中断它。
以下代码允许(有一些限制)在python线程中引发异常:
(基于tomer filiba的killable threads。有关的返回值的引号
PyThreadState_SetAsyncExc
似乎来自旧版本的python。)正如文档中所指出的,这并不是一个灵丹妙药,因为如果线程在python解释器之外忙碌,它将无法捕获中断。
这段代码的一个好的使用模式是让线程捕获特定的异常并执行清理。这样,您就可以中断一个任务,并且仍然可以进行适当的清理。
qvtsj1bj10#
没有官方的api可以做到这一点。
您需要使用平台api终止线程,例如pthread_kill或terminatethread。您可以通过pythonwin或ctypes访问此类api。
请注意,这本身就是不安全的。它可能会导致无法收集的垃圾(来自成为垃圾的堆栈帧的局部变量),并可能导致死锁,如果被终止的线程在终止时具有gil。
mhd8tkvw11#
A.
multiprocessing.Process
可以p.terminate()
如果我想杀死一个线程,但不想使用标志/锁/信号/信号量/事件/任何东西,我会将线程升级为完整的进程。对于只使用几个线程的代码,开销并没有那么大。e、 这样可以方便地终止执行阻塞i/o的助手“线程”
转换很简单:在相关代码中替换所有
threading.Thread
具有multiprocessing.Process
诸如此类queue.Queue
具有multiprocessing.Queue
并添加所需的p.terminate()
要杀死其子进程的父进程p
有关详细信息,请参阅python文档multiprocessing
.例子:
7cjasjjr12#
如果您试图终止整个程序,可以将线程设置为“守护进程”。请参阅thread.daemon
93ze6v8z13#
正如其他人所提到的,标准是设置停止标志。对于一些轻量级的东西(没有线程的子类,没有全局变量),lambda回调是一个选项(请注意中的括号
if stop()
.)替换
print()
用一个pr()
总是刷新的函数(sys.stdout.flush()
)可以提高外壳输出的精度。(仅在windows/eclipse/python3.3上测试)
nwo49xxi14#
这是基于thread2——可终止线程(python配方)
您需要调用pythreadstate_setasyncexc(),该函数仅可通过ctypes使用。
这只在Python2.7.3上进行了测试,但它可能适用于最近的其他2.x版本。
3qpi33ja15#
在python中,不能直接终止线程。
如果您真的不需要线程(!),您可以做的是使用多处理包,而不是使用线程包。在这里,要终止进程,只需调用以下方法:
python将终止您的进程(在unix上通过sigterm信号,而在windows上通过
TerminateProcess()
电话)。使用队列或管道时请注意使用它(它可能会损坏队列/管道中的数据)请注意
multiprocessing.Event
和multiprocessing.Semaphore
以完全相同的方式工作threading.Event
和threading.Semaphore
分别地事实上,第一批是latters的克隆。如果你真的需要使用线程,没有办法直接杀死它。但是,您可以做的是使用“守护线程”。事实上,在python中,线程可以标记为守护进程:
当没有活动的非守护进程线程时,主程序将退出。换句话说,当主线程(当然是非守护进程线程)将完成其操作时,即使仍有一些守护进程线程在工作,程序也将退出。
请注意,有必要将线程设置为
daemon
在start()
方法被调用!当然,你可以也应该使用
daemon
即使multiprocessing
. 在这里,当主进程退出时,它会尝试终止其所有daemonic子进程。最后,请注意
sys.exit()
及os.kill()
这不是选择。