python—有没有办法杀死线程?

cngwdvgl  于 2021-08-25  发布在  Java
关注(0)|答案(20)|浏览(532)

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

iyzzxitl

iyzzxitl1#

在python和任何语言中,突然终止线程通常都是不好的模式。请考虑以下情况:
线程持有一个必须正确关闭的关键资源
该线程还创建了其他几个必须终止的线程。
如果您负担得起(如果您正在管理自己的线程),处理这个问题的好方法是使用一个exit_请求标志,每个线程定期检查该标志,以查看是否是退出的时间。
例如:

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() . 线程应定期检查停止标志。
但是,在某些情况下,确实需要终止线程。例如,当您正在 Package 一个因长时间呼叫而忙碌的外部库时,您想中断它。
以下代码允许(有一些限制)在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解释器之外忙碌,它将无法捕获中断。
这段代码的一个好的使用模式是让线程捕获特定的异常并执行清理。这样,您就可以中断一个任务,并且仍然可以进行适当的清理。

r6vfmomb

r6vfmomb2#

没有官方的api可以做到这一点。
您需要使用平台api终止线程,例如pthread_kill或terminatethread。您可以通过pythonwin或ctypes访问此类api。
请注意,这本身就是不安全的。它可能会导致无法收集的垃圾(来自成为垃圾的堆栈帧的局部变量),并可能导致死锁,如果被终止的线程在终止时具有gil。

gcuhipw9

gcuhipw93#

A. multiprocessing.Process 可以 p.terminate() 如果我想杀死一个线程,但不想使用标志/锁/信号/信号量/事件/任何东西,我会将线程升级为完整的进程。对于只使用几个线程的代码,开销并没有那么大。
e、 这样可以方便地终止执行阻塞i/o的助手“线程”
转换很简单:在相关代码中替换所有 threading.Thread 具有 multiprocessing.Process 诸如此类 queue.Queue 具有 multiprocessing.Queue 并添加所需的 p.terminate() 要杀死其子进程的父进程 p 有关详细信息,请参阅python文档 multiprocessing .
例子:

import multiprocessing
proc = multiprocessing.Process(target=your_proc_function, args=())
proc.start()

# Terminate the process

proc.terminate()  # sends a SIGTERM
bis0qfac

bis0qfac4#

如果您试图终止整个程序,可以将线程设置为“守护进程”。请参阅thread.daemon

ozxc1zmp

ozxc1zmp5#

正如其他人所提到的,标准是设置停止标志。对于一些轻量级的东西(没有线程的子类,没有全局变量),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() )可以提高外壳输出的精度。
(仅在windows/eclipse/python3.3上测试)

mw3dktmi

mw3dktmi6#

这是基于thread2——可终止线程(python配方)
您需要调用pythreadstate_setasyncexc(),该函数仅可通过ctypes使用。
这只在Python2.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")
olmpazwi

olmpazwi7#

在python中,不能直接终止线程。
如果您真的不需要线程(!),您可以做的是使用多处理包,而不是使用线程包。在这里,要终止进程,只需调用以下方法:

yourProcess.terminate()  # kill the process!

python将终止您的进程(在unix上通过sigterm信号,而在windows上通过 TerminateProcess() 电话)。使用队列或管道时请注意使用它(它可能会损坏队列/管道中的数据)
请注意 multiprocessing.Eventmultiprocessing.Semaphore 以完全相同的方式工作 threading.Eventthreading.Semaphore 分别地事实上,第一批是latters的克隆。
如果你真的需要使用线程,没有办法直接杀死它。但是,您可以做的是使用“守护线程”。事实上,在python中,线程可以标记为守护进程:

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

当没有活动的非守护进程线程时,主程序将退出。换句话说,当主线程(当然是非守护进程线程)将完成其操作时,即使仍有一些守护进程线程在工作,程序也将退出。
请注意,有必要将线程设置为 daemonstart() 方法被调用!
当然,你可以也应该使用 daemon 即使 multiprocessing . 在这里,当主进程退出时,它会尝试终止其所有daemonic子进程。
最后,请注意 sys.exit()os.kill() 这不是选择。

gcmastyq

gcmastyq8#

在不与线程合作的情况下,决不能强行杀死线程。
终止一个线程将删除try/finally阻塞设置的任何保证,这样您就可以保持锁锁定、文件打开等状态。
你唯一可以说强制杀死线程是个好主意的时候就是快速杀死一个程序,但绝不是单个线程。

ubby3x7f

ubby3x7f9#

如果您明确地呼叫 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() (ing)在我看来,处理退出的代码要简单得多。

1wnzp6jl

1wnzp6jl10#

您可以通过将跟踪安装到将退出线程的线程中来终止线程。有关一种可能的实现方式,请参见所附链接。
在python中杀死线程

x6h2sr28

x6h2sr2811#

最好不要断线。一种方法是在线程的循环中引入一个“try”块,并在想要停止线程时抛出一个异常(例如break/return/…)。。。这将停止您的操作(for/while/…)。我已经在我的应用程序中使用了它,它可以工作。。。

yxyvkwin

yxyvkwin12#

实施一个 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 班级。

bwitn5fc

bwitn5fc13#

我玩这个游戏已经晚了很久,但我一直在努力解决一个类似的问题,下面的内容似乎可以完美地解决这个问题,并让我在守护子线程退出时执行一些基本的线程状态检查和清理:

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]
bweufnob

bweufnob14#

以下变通方法可用于终止线程:

kill_threads = False

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

thread.start_new_thread(doSomething, ())

这甚至可以用于从主线程终止线程(其代码写在另一个模块中)。我们可以在该模块中声明一个全局变量,并使用它终止在该模块中生成的线程。
我通常使用它来终止程序出口处的所有线程。这可能不是终止线程的完美方式,但可能会有所帮助。

dl5txlt9

dl5txlt915#

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

这是你的 Thread 对象
阅读python源代码( Modules/threadmodule.cPython/thread_pthread.h )你可以看到 Thread.ident 是一个 pthread_t 输入,这样你就可以做任何事情 pthread 能在python中使用吗 libpthread .

相关问题