当前位置:  开发笔记 > 编程语言 > 正文

有没有办法杀死一个线程?

如何解决《有没有办法杀死一个线程?》经验,为你挑选了15个好方法。

是否可以在不设置/检查任何标志/信号量/等的情况下终止正在运行的线程?



1> Philippe F..:

在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 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.关于返回值的引用stop()似乎来自旧版本的Python.)

如文档中所述,这不是一个神奇的子弹,因为如果线程在Python解释器之外繁忙,它将不会捕获中断.

此代码的良好使用模式是让线程捕获特定异常并执行清理.这样,您可以中断任务并仍然进行适当的清理.


@ Bluebird75:此外,我不确定我是否认为线程不应该被突然杀死"因为线程可能持有必须正确关闭的关键资源":主程序和主程序也是如此可以被用户突然杀死(例如在Unix中使用Ctrl-C) - 在这种情况下,他们尽可能地尽可能地处理这种可能性.因此,我没有看到线程的特殊之处,以及为什么它们不应该像主程序那样得到相同的处理(即它们可以突然被杀死).你能详细说明一下吗?
值得一提的是,_stop已经在Python 3线程库中占用了.因此,可能使用不同的变量,否则您将收到错误.
@EOL:另一方面,如果线程拥有的所有资源都是本地资源(打开文件,套接字),那么Linux在进程清理方面相当不错,而且不会泄漏.虽然我使用套接字创建了一个服务器,但是如果我使用Ctrl-C进行残酷的中断,我可以不再启动程序,因为它无法绑定套接字.我需要等5分钟.正确的解决方案是捕获Ctrl-C并清除套接字断开连接.
注意这个答案:至少对我来说(py2.6),我必须为`res!= 1`情况传递`None`而不是'0`,我不得不调用`ctypes.c_long(tid)`并将其传递给任何ctypes函数而不是直接传递给tid.
@Bluebird75:顺便说一下.您可以使用`SO_REUSEADDR`套接字选项来避免"Address in in use"错误.
这段代码不太有效.您需要将tid转换为long,如:ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),None)
@EOL:在我记忆中,使用线程没有特定的优势.事件确实存在.
我不同意.当你说"杀死一个线程"时 - 你的意思是"向线程发送信号".(这可能是神奇的#9,或者最好是另一个).有很多时候这是有道理的.例如,如果线程处于某种阻塞系统调用中,则需要*能够向其发送信号以将其弹出,即使与"优雅"机制一起退出[轮询]线程也是如此.
我喜欢'StoppableThread`方法.我添加了一个类`ThreadKilled(KeyboardInterrupt):pass`并添加了`def sleep(self,time):self._stop.wait(time); 如果self.stopped():提高ThreadKilled`.这样,只要线程用`self.sleep(t)`替换对`time.sleep(t)`的调用,线程就可以从长时间的睡眠中被杀死.从那里开始,我继续使用`Queue.Queue`代替`threading.Event`,因为它提供相同的基本功能(包括`sleep(t)`),但也让线程成为消费者.
@ Bluebird75:在第一个例子中,为什么要使用`threading.Event`而不是可以从线程内部检查的简单布尔属性`stopped`?
@Ben这是因为在线程中引发异常时,这是一个Python异常,只能通过Python解释器进行访问。由于time.sleep是释放GIL的外部C函数,因此直到该函数返回,重新获取GIL并运行Python字节码解释器后,它才会意识到它已发生异常。Ctrl + C会触发系统级信号,然后将其转换为Python异常。这就是为什么它可以与“ time.sleep”一起使用的原因。

2> Martin v. Lö..:

没有官方API可以做到这一点,没有.

您需要使用平台API来终止线程,例如pthread_kill或TerminateThread.您可以通过pythonwin或ctypes访问此类API.

请注意,这本质上是不安全的.它可能会导致无法收集的垃圾(来自堆栈帧的局部变量变成垃圾),并且如果被杀死的线程在被杀死时具有GIL,则可能导致死锁.


如果有问题的线程持有GIL,它*将导致死锁.

3> cfi..:

一个multiprocessing.ProcessCANp.terminate()

在我想杀死一个线程但不想使用flags/locks/signals/semaphores/events /的情况下,我将线程提升为完整的进程.对于仅使用几个线程的代码,开销并不是那么糟糕.

例如,这可以轻松终止执行阻塞I/O的帮助程序"线程"

转换很简单:在相关的代码替换所有threading.Threadmultiprocessing.Process所有queue.Queuemultiprocessing.Queue,并添加所需的呼叫p.terminate()到要杀死其子你的父进程p

Python文档


`multiprocessing`很好,但要注意参数被腌制到新进程.因此,如果其中一个参数是不可选择的(如`logging.log`),那么使用`multiprocessing`可能不是一个好主意.

4> schettino72..:

如果您尝试终止整个程序,可以将该线程设置为"守护程序".请参阅 Thread.daemon


Raffi我认为他建议你提前设置它,知道当你的主线程退出时你也希望守护程序线程退出.

5> Johan Dahlin..:

这是基于thread2 - killable线程(Python配方)

您需要调用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")



6> Jon Coombs..:

正如其他人所提到的,常规是设置一个停止标志.对于轻量级的东西(没有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上测试)



7> angry person..:

你不应该在没有合作的情况下强行杀死一个线程.

杀死一个线程会删除尝试/最终阻止设置的任何保证,这样你就可以锁定锁,打开文件等.

唯一一次你可以争辩强行杀死线程是一个好主意是快速杀死程序,但绝不是单线程.


为什么要告诉一个主题是如此困难,当你完成当前循环时请自杀...我不明白.
在cpu中没有内置机制来识别"循环"本身,你可以期望的最好的方法是使用某种信号,使得当前循环内的代码一旦退出就会检查.处理线程同步的正确方法是通过协作方式,线程的暂停,恢复和终止是用于调试器和操作系统的功能,而不是应用程序代码.
@Mehdi:如果我(亲自)在线程中编写代码,是的,我同意你的观点.但有些情况下我正在运行第三方库,而且我无法访问该代码的执行循环.这是所请求功能的一个用例.

8> Paolo Rovell..:

在Python中,你根本无法直接杀死一个线程.

如果你真的不需要一个Thread(!),你可以做的,而不是使用线程包,就是使用 多处理包.在这里,要杀死进程,您只需调用该方法:

yourProcess.terminate()  # kill the process!

Python将终止你的进程(在Unix上通过SIGTERM信号,而在Windows上通过TerminateProcess()调用).使用队列或管道时要注意使用它!(它可能会破坏队列/管道中的数据)

需要注意的是,multiprocessing.Eventmultiprocessing.Semaphore中的相同的方式工作,准确threading.Eventthreading.Semaphore分别.事实上,第一个是后者的克隆.

如果您真的需要使用Thread,则无法直接杀死它.但是,您可以使用"守护程序线程".事实上,在Python中,Thread可以被标记为守护进程:

yourThread.daemon = True  # set the Thread as a "daemon thread"

当没有剩下活着的非守护程序线程时,主程序将退出.换句话说,当您的主线程(当然是非守护程序线程)将完成其操作时,即使仍有一些守护程序线程正在运行,程序也将退出.

请注意,必须daemonstart()调用方法之前设置Thread !

当然,你可以而且应该使用daemonmultiprocessing.这里,当主进程退出时,它会尝试终止其所有守护进程子进程.

最后,请注意sys.exit()并且os.kill()不是选择.



9> Kozyarchuk..:

您可以通过在将退出线程的线程中安装trace来终止线程.有关可能的实现,请参阅附件链接.

用Python杀死一个线程


此解决方案存在两个问题:(a)使用sys.settrace()安装跟踪器将使您的线程运行速度变慢.如果它的计算界限慢多达10倍.(b)在系统调用时不会影响您的线程.

10> Giancarlo..:

如果你不杀死一个线程会更好.一种方法可能是在线程的循环中引入一个"try"块,并在你想要停止线程时抛出一个异常(例如一个break/return/...停止你的for/while/...).我在我的应用程序上使用过这个功能......



11> Noctis Skyto..:

绝对可以实现一个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`注入检查的一种聪明方法.请注意,与此处的大多数其他解决方案一样,它实际上不会中断阻塞调用,因为只有在输入新的本地范围时才会调用跟踪函数.另外值得注意的是,`sys.settrace`确实意味着实现调试器,配置文件等,因此被认为是CPython的实现细节,并不保证在其他Python实现中存在.
@dano:`Thread2`类最大的问题之一是它运行的代码大约慢了十倍.有些人可能仍然认为这是可以接受的

12> snyh..:
from ctypes import *
pthread = cdll.LoadLibrary("libpthread-2.15.so")
pthread.pthread_cancel(c_ulong(t.ident))

t是你的Thread对象.

阅读python源代码(Modules/threadmodule.cPython/thread_pthread.h)你可以看到它Thread.ident是一个pthread_t类型,所以你可以做任何事情都pthread可以在python中使用libpthread.


你没有; 不是在Windows上,也不在Linux上.原因:当您执行此操作时,有问题的线程可能会保留GIL(当您调用C时,Python会释放GIL).如果是这样,您的程序将立即陷入僵局.即使它没有,最后:块也不会被执行等等,所以这是一个非常不安全的想法.

13> Amit Chahar..:

可以使用以下解决方法杀死线程:

kill_threads = False

def doSomething():
    global kill_threads
    while True:
        if kill_threads:
            thread.exit()
        ......
        ......

thread.start_new_thread(doSomething, ())

这甚至可以用于终止从主线程终止其代码在另一个模块中编写的线程。我们可以在该模块中声明一个全局变量,并使用它终止该模块中产生的线程。

我通常使用它在程序出口处终止所有线程。这可能不是终止线程的理想方法,但可能会有所帮助。



14> SCB..:

如果你是显式调用time.sleep()为你的线程(比如查询一些外部的服务)的一部分,在菲利普的方法的改进是使用了超时的eventwait()方法,无论你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时),并且我认为处理退出的代码明显更简单。


为什么这篇文章被否决?这个帖子有什么问题?看起来完全符合我的需求。

15> slumtrimpet..:

我玩这个游戏很晚,但是我一直在努力解决类似的问题,以下内容似乎可以为我很好地解决问题,并且可以在守护子线程退出时让我进行一些基本的线程状态检查和清理:

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]

推荐阅读
135369一生真爱_890
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有