有什么办法可以杀死线程吗?


Answers:


672

用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解释器之外很忙,它将无法捕获中断。

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


77
@ Bluebird75:此外,我不确定是否应该突然终止线程的论点“因为线程可能持有必须正确关闭的关键资源”:在主程序和主程序中也是如此可能会被用户突然杀死(例如,在Unix中为Ctrl-C)-在这种情况下,他们会尝试尽可能好地处理这种可能性。因此,我看不到线程有什么特别之处,以及为什么它们不应该与主程序得到同等待遇(即它们可能会被突然杀死)。:)您能详细说明一下吗?
Eric O Lebigot 2011年

18
@EOL:另一方面,如果线程拥有的所有资源都是本地资源(打开文件,套接字),则Linux在处理进程方面相当出色,并且不会泄漏。我遇到过使用套接字创建服务器的情况,如果我用Ctrl-C进行了残酷的中断,我将无法再启动该程序,因为它无法绑定套接字。我需要等待5分钟。正确的解决方案是捕获Ctrl-C并清除套接字的连接。
菲利普·F

10
@ Bluebird75:顺便说一句。您可以使用SO_REUSEADDR套接字选项来避免Address already in use错误。
Messa

12
请注意有关此答案的信息:至少对我来说(py2.6),我必须传递None而不是0针对这种res != 1情况,并且我必须调用ctypes.c_long(tid)并将其传递给任何ctypes函数,而不是直接传递给tid。
Walt W

21
值得一提的是_stop已被Python 3线程库占用。因此,请使用其他变量,否则会出现错误。
13年

113

没有官方的API可以这样做。

您需要使用平台API杀死线程,例如pthread_kill或TerminateThread。您可以通过pythonwin或ctypes访问此类API。

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


26
如果相关线程持有GIL ,导致死锁。
Matthias Urlichs 2015年

95

一个multiprocessing.Process罐头p.terminate()

在我想杀死一个线程,但又不想使用标志/锁/信号/信号量/事件/任何东西的情况下,我会将线程提升为完整进程。对于仅使用几个线程的代码,开销并不是那么糟糕。

例如,这样做很方便,可以轻松终止执行阻塞I / O的助手“线程”

转换是微不足道的:在相关代码中,将全部替换threading.Threadmultiprocessing.Process和全部替换queue.Queuemultiprocessing.Queue,并将所需的调用添加p.terminate()到要杀死其子级的父进程中p

请参阅Python文档multiprocessing


谢谢。我用multiprocessing.JoinableQueue替换了queue.Queue并遵循了这个答案:stackoverflow.com/a/11984760/911207
David Braun

关于此问题的页面很多。对于许多我认为,这似乎是显而易见的解决方案
geotheory,2016年

6
multiprocessing很好,但请注意,将参数腌制到新过程中。因此,如果其中一个参数是不可选择的东西(如a logging.log),则使用它可能不是一个好主意multiprocessing
Lyager

1
multiprocessing参数在Windows上被添加到新进程中,但是Linux使用分叉来复制它们(Python 3.7,不确定其他版本)。因此,您将获得在Linux上可以使用的代码,但在Windows上却出现了pickle错误。
nyanpasu64

multiprocessing日志记录是一件棘手的事情。需要使用QueueHandler(请参阅本教程)。我很难学。
Fanchen Bao

74

如果试图终止整个程序,则可以将线程设置为“守护程序”。参见 Thread.daemon


这没有任何意义。该文档明确指出:“必须在调用start()之前设置此项,否则会引发RuntimeError。” 因此,如果我想杀死最初不是守护程序的线程,该如何使用?
拉菲·哈查杜安

27
Raffi我认为他建议您提前设置它,因为知道在您的主线程退出时,您还希望守护程序线程退出。
2014年

1
不确定为什么这不是公认的答案
eric

如果希望即使主程序关闭,线程仍继续运行,您是否会设置线程作为守护程序呢?
Michele Piccolini

先生,您是我今天的英雄。正是我要找的东西,一点也不麻烦。
Blizz

42

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


1
在Linux / Python 2.7上进行了验证,就像魅力一样。这应该是官方的答案,它要简单得多。
Paul Kenjora '17

1
在Linux Ubuntu Server 17.10 / Python 3.6.3上进行了验证并可以运行。
马科斯(Marcos)

1
也已在2.7中验证。这么好的答案!
silgon

什么是pr()功能?
alper

35

这基于thread2-可杀死的线程(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")

我正在使用类似的东西给我的线程一个线程,KeyboardInterrupt以便它们有机会清理。如果它们在此之后仍然挂起,则SystemExit是适当的,或者只是从终端中终止该进程。
drevicko

如果线程当前正在执行,则此方法有效。如果线程在syscall中,则它不起作用。异常将被忽略。
Matthias Urlichs 2014年

@MatthiasUrlichs知道如何检测线程执行状态是什么,以便能够打印警告或重试吗?
Johan Dahlin 2014年

1
@JohanDahlin您可以稍等一下(如果要重试,则仍然需要这样做),然后再进行isAlive()测试。无论如何,尽管这行得通,但我也不保证它不会留下悬挂的引用。从理论上讲,可以通过明智地使用来确保CPython中线程杀死的安全性pthread_cleanup_push()/_pop(),但要正确实现,将需要进行大量工作,这将大大降低解释器的速度。
Matthias Urlichs 2014年

32

如果不配合使用线程,切勿强行杀死它。

杀死线程会删除所有尝试/最终阻止设置的保证,因此您可能会锁定锁,打开文件等。

唯一可以断言强行杀死线程是个好主意的方法是快速杀死程序,但绝不要杀死单个线程。


11
为什么仅仅告诉一个线程为什么这么难,请在完成当前循环后杀死自己……我不明白。
Mehdi

2
cpu中没有内置这样的机制来识别“循环”,您可以希望最好的办法是使用某种信号,使循环中当前存在的代码一旦退出就将对其进行检查。处理线程同步的正确方法是通过协作方式进行,线程的挂起,恢复和终止是调试器和操作系统使用的功能,而不是应用程序代码。
Lasse V. Karlsen

2
@Mehdi:如果我(个人)在线程中编写代码,是的,我同意你的观点。但是在某些情况下,我正在运行第三方库,但无法访问该代码的执行循环。这是所请求功能的一种用例。
Dan H

@DanH 对于第三方代码,这甚至更糟,因为您不知道它会造成什么损害。如果您的第三方库不够强大,无法杀死它,那么您应该执行以下操作之一:(1)请作者解决问题,(2)使用其他方法。如果您真的别无选择,那么将代码放在不同的流程中应该更安全,因为某些资源仅在单个流程中共享。
Phil1970

25

在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"

当没有活动的非守护线程时,主程序将退出。换句话说,当您的主线程(当然,这是一个非守护程序线程)完成其操作时,即使仍有一些守护程序线程在工作,程序也会退出。

请注意,有必要daemonstart()调用该方法之前设置一个线程!

当然,daemon甚至可以使用multiprocessing。在这里,当主进程退出时,它将尝试终止其所有守护进程。

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


14

您可以通过在将退出线程的线程中安装跟踪来杀死线程。请参阅附件链接以获取一种可能的实现。

在Python中杀死线程


我已经看过了 该解决方案基于自检标志检查
Sudden Def

1
实际上
起作用

5
此解决方案有两个问题:(a)使用sys.settrace()安装跟踪程序会使线程运行速度变慢。如果受到计算限制,速度会慢10倍。(b)在系统调用中不会影响您的线程。
Matthias Urlichs 2014年

10

如果你是显式调用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),并且我认为处理退出的代码明显更简单。


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

9

如果您不杀死线程,那就更好了。一种方法可能是在线程的循环中引入“ try”块,并在您要停止线程时引发异常(例如,break / return / ...会停止for / while / ...)。我已经在我的应用程序上使用了它,并且可以使用...


8

绝对有可能实现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类。


3
这是将检查self.__stop设置为线程的聪明方法。请注意,与此处的大多数其他解决方案一样,它实际上不会中断阻塞调用,因为仅当输入新的本地作用域时才调用trace函数。同样值得注意的是,它sys.settrace确实意味着要实现调试器,配置文件等,因此被视为CPython的实现细节,并且不能保证在其他Python实现中也存在。
dano

3
@dano:Thread2该类的最大问题之一是它运行的代码慢了大约十倍。有些人可能仍然认为这可以接受。
Noctis Skytower

+1会大大减慢代码的执行速度。.我建议该解决方案的作者将此信息包含在答案中。
维沙尔(Vishal)

6
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 use中做任何事情libpthread


以及如何在Windows上执行此操作?
iChux

12
你不 不在Windows上也不在Linux上。原因:执行该操作时,相关线程可能会容纳GIL(当您调用C时,Python会释放GIL)。如果是这样,您的程序将立即死锁。即使没有,最终:块也不会被执行,等等,所以这是一个非常不安全的想法。
Matthias Urlichs 2014年

6

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

kill_threads = False

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

thread.start_new_thread(doSomething, ())

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

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


竖起大拇指。简单易懂。
alyssaeliyah

6

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

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]

这是一个很好的答案,应该在列表上更高
drootang

为什么SystemExitafter_timeout线程上引发对主线程有任何作用(在此示例中,主线程只是在等待前者退出)?
戴维斯·鲱鱼

@DavisHerring我不确定你在做什么。SystemExit杀死了主线程,为什么您认为它会在主线程上做任何事情?没有该调用,该程序将仅继续等待子线程。您也可以按ctrl + c或使用任何其他方式杀死主线程,但这只是一个示例。
slumtrimpet

@slumtrimpet:SystemExit仅具有两个特殊属性:它不产生回溯(当任何线程抛出一个异常退出时),并且如果线程抛出一个异常退出,则它设置退出状态(尽管如此,它仍在等待其他非守护进程线程)退出)。
戴维斯鲱鱼

@DavisHerring哦,我明白了。完全同意你的看法。我记错了我们在这里所做的事情,并且误解了您的评论。
slumtrimpet

4

我想补充的一件事是,如果您在线程lib Python中阅读了官方文档,建议您避免使用“恶魔”线程,如果您不希望线程突然结束,请使用Paolo Rovelli 提到的标志。

根据官方文档:

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

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

永远记住,如果您有“活动线程”,Python解释器将运行以等待它们。(因为这个守护进程可以帮助您,如果没关系突然结束)。


4
我不明白最后一段。
tshepang 2014年

@Tshepang这意味着,如果在你的应用程序的任何运行非守护线程,Python解释器将继续运行,直到所有非守护线程完成。如果您不关心线程在程序终止时是否结束,则可以使它们成为守护程序。
汤姆·迈德丁

3

有一个为此目的而建立的库stopit。尽管此处列出的某些注意事项仍然适用,但至少此库提供了一种常规的,可重复的技术来实现所述目标。


1

虽然它已经很老了,对于某些人来说可能是一个方便的解决方案:

一个扩展了线程的模块功能的小模块-允许一个线程在另一个线程的上下文中引发异常。通过提高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()也会存在一个问题,这将有效地取消您的未决异常。您可以尝试再次提高它。
  • 只有异常类型可以安全地引发。异常实例可能会导致意外行为,因此受到限制。
  • 我要求在内置线程模块中公开此功能,但是由于ctypes已成为标准库(从2.5版开始),并且此
    功能不太可能与实现无关,因此可以不公开

0

这似乎与Windows 7上的pywin32一起使用

my_thread = threading.Thread()
my_thread.start()
my_thread._Thread__stop()

0

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

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

这包括告诉子线程应该取消其工作。这可以通过为该线程配备一个ØMQ套接字并在该套接字上轮询以获取一条消息指出该线程应取消的信息来完成。

该链接还提供了有关带有ØMQ的多线程python代码的示例。


0

假设您要具有多个具有相同功能的线程,这是恕我直言,最简单的实现是通过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 ,则停止。


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”))时将使用的参数。


0

如@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并打印后停止23不打印。


-1

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

-1

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

-2

这是一个错误的答案,请参阅评论

方法如下:

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


我必须在我的threading.Thread对象中使用self._Thread__stop()使其停止。我不明白为什么像这样的示例(code.activestate.com/recipes/65448-thread-control-idiom)这样的简单self.join()无法正常工作。
harijay 2011年

12
有关“这不会真正停止线程”的更多详细信息将很有帮助。
2371

19
基本上,调用_Thread__stop方法除了告诉Python线程已停止外没有任何作用。它实际上可以继续运行。有关示例,请参见gist.github.com/2787191
Bluehorn,2012年

35
这是完全错误的。_Thread__stop()只是将一个线程标记为已停止,实际上并没有停止该线程!永远不要这样做。阅读
dotancohen

-2

如果您确实需要杀死子任务的能力,请使用替代实现。multiprocessing并且gevent都支持滥杀“线程”。

Python的线程不支持取消。想都别想。您的代码很可能会死锁,损坏或泄漏内存,或者具有其他意想不到的“有趣”难以调试的效果,这种情况很少且不确定地发生。


2
…是的,我知道两者都不严格是“线程化”的,但是只要您的代码适合(或可以使其适合)他们的模型,它们都可以使用。
Matthias Urlichs 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.