首页 文章

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

提问于
浏览
614

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

24 回答

  • 4

    Pieter Hintjens-- ØMQ项目的创始人之一 - 说,使用ØMQ并避免锁,互斥,事件等同步原语,是编写多线程程序的最安全和最安全的方法:

    http://zguide.zeromq.org/py:all#Multithreading-with-ZeroMQ

    这包括告诉子线程,它应该取消它的工作 . 这可以通过为线程配备一个ØMQ套接字并在该套接字上轮询一条消息说它应该取消来完成 .

    该链接还提供了一个使用ØMQ的多线程python代码的示例 .

  • 2

    这似乎适用于Windows 7上的pywin32

    my_thread = threading.Thread()
    my_thread.start()
    my_thread._Thread__stop()
    
  • 26
    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 .

  • -1

    这是基于thread2 -- killable threads (Python recipe)

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

    虽然它相当陈旧,但对于某些人来说,this可能是一个方便的解决方案:

    一个扩展线程模块功能的小模块 - 允许一个线程在另一个线程的上下文中引发异常 . 通过提升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)
    

    因此,它允许“线程在另一个线程的上下文中引发异常”,并且通过这种方式,终止的线程可以处理终止而无需定期检查中止标志 .

    但是,根据其original source,此代码存在一些问题 .

    只有在执行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)开始,这个特征不太可能与实现无关,它可能保持未暴露 .

  • 7

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

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

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

  • 19

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

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

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

  • 7

    如果你明确地调用 time.sleep() 作为你的线程的一部分(比如轮询一些外部服务),Phillipe方法的一个改进就是在 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() 并定期检查事件的优点是你可以在较长的睡眠间隔内编程,线程几乎立即停止(否则你将被 sleep() ),在我看来,处理退出的代码非常简单 .

  • 30

    为此目的 Build 了一个图书馆,stopit . 尽管本文列出的一些相同注意事项仍然适用,但至少该库提供了用于实现所述目标的常规,可重复的技术 .

  • 102

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

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

    yourProcess.terminate()  # kill the process!
    

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

    请注意, multiprocessing.Eventmultiprocessing.Semaphore 的工作方式与 threading.Eventthreading.Semaphore 完全相同 . 事实上,第一个是后者的克隆 .

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

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

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

    请注意,在调用 start() 方法之前,必须将Thread设置为 daemon

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

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

  • 13

    使用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
    
  • 9

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

    Kill a thread in Python

  • 2

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

  • 30

    如果您尝试终止整个程序,可以将线程设置为"daemon" . 见Thread.daemon

  • 562

    我'm way late to this game, but I'已经和a similar question搏斗了,以下似乎都能完美地解决这个问题并让我在守护进程的子线程退出时做一些基本的线程状态检查和清理:

    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]
    
  • 1

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

  • 0

    绝对可以实现 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% .

  • 0

    这是一个糟糕的答案,请参阅评论

    这是怎么做的:

    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() .

  • 59

    在Python和任何语言中突然杀死一个线程通常是一种糟糕的模式 . 想想以下情况:

    • 线程持有必须正确关闭的关键资源

    • 线程已经创建了几个必须被杀死的其他线程 .

    如果你负担得起它(如果你正在管理你自己的线程),处理这个的好方法是有一个exit_request标志,每个线程定期检查它是否有时间退出 .

    For example:

    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线程中引发异常:

    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 . 关于 PyThreadState_SetAsyncExc 的返回值的引用似乎来自old version of Python . )

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

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

  • 1

    multiprocessing.Process 可以 p.terminate()

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

    例如 . 这样可以轻松终止执行阻塞I / O的帮助程序“线程”

    转换是微不足道的:在相关代码中将所有 threading.Thread 替换为 multiprocessing.Process 并将所有 queue.Queue 替换为 multiprocessing.Queue 并将 p.terminate() 的所需调用添加到您想要杀死其子进程的父进程 p

    Python doc

  • 0

    您可以在进程中执行命令,然后使用进程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;
    
  • 0

    我想补充的一件事是,如果你在threading lib Python中阅读官方文档,那么's recommended to avoid use of 94265 threads, when you don' t希望线程突然结束,标志是Paolo Rovelli mentioned .

    从官方文件:

    守护程序线程突然停止在关机 . 他们的资源(例如打开文件,数据库事务等)可能无法正确发布 . 如果您希望线程正常停止,请将它们设置为非守护进程并使用合适的信号机制(如Event) .

    我认为创建守护线程取决于你的应用程序,但总的来说(在我看来)最好避免杀死它们或使它们成为守护进程 . 在多处理中,您可以使用 is_alive() 来检查进程状态,使用"terminate"来完成它们(还可以避免GIL问题) . 但是,当您在Windows中执行代码时,有时会发现更多问题 .

    并且永远记住,如果你有“活动线程”,Python解释器将运行等待它们 . (因为这个守护神可以帮助你,如果无所谓突然结束) .

  • 65

    如果您确实需要能够终止子任务,请使用备用实现 . multiprocessinggevent 都支持不分青红皂白地杀死"thread" .

    Python的线程不支持取消 . 想都别想 . 您的代码很可能会死锁,损坏或泄漏内存,或者有其他无意识的“有趣”难以调试的影响,这些影响很少发生且不确定 .

  • -2

    以下解决方法可用于终止线程:

    kill_threads = False
    
    def doSomething():
        global kill_threads
        while True:
            if kill_threads:
                thread.exit()
            ......
            ......
    
    thread.start_new_thread(doSomething, ())
    

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

    我通常使用它来终止程序出口处的所有线程 . 这可能不是终止线程的完美方式,但可能有所帮助 .

相关问题