是否有可能终止一个正在运行的线程而不设置/检查任何标志/信号/等等?
当前回答
这是一个糟糕的答案,请看评论
以下是如何做到这一点:
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()方法,终止套接字,然后在你的quit()中运行一个thread. _thread__stop()。
其他回答
您可以在进程中执行命令,然后使用进程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;
可以通过在将退出线程的线程中安装trace来终止线程。请参阅所附的链接,了解一种可能的实现。
在Python中杀死一个线程
正如@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不打印。
在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线程中引发异常:
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。)
正如文档中所指出的,这不是一个神奇的子弹,因为如果线程在Python解释器之外忙,它将不会捕获中断。
这段代码的一个良好使用模式是让线程捕获特定的异常并执行清理。这样,您可以中断任务,但仍然可以进行适当的清理。
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))
t是线程对象。
阅读python源代码(Modules/threadmodule.c和python /thread_pthread.h),你可以看到Thread。Ident是一个pthread_t类型,所以你可以使用libpthread在python中做任何pthread可以做的事情。