python - 停止线程 - 获取无效的线程 ID

标签 python multithreading python-3.x python-multithreading

这个问题之前已经被回答过,我正在尝试在 first answer outlined here 中实现第二个解决方案。 (注意:第一个解决方案与我无关,我的线程正在从外部库运行服务器,并且无法修改以检查标志)

我尝试实现符合我的情况的最简单的情况。我有一个生成线程的类,并且该线程应该在外部停止(该线程永远不会自然完成,如本例所示)。 注意:_async_raiseThreadWithExc 是此问题的已接受答案的复制/粘贴:

import threading
import inspect
import ctypes
import time

# https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python
def _async_raise(tid, exctype):
    if not inspect.isclass(exctype):
        raise TypeError("Only types can be raised (not instances)")
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
                                                  ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")

# https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python
class ThreadWithExc(threading.Thread):
    def _get_my_tid(self):

        if not self.isAlive():
            raise threading.ThreadError("the thread is not active")

        if hasattr(self, "_thread_id"):
            return self._thread_id

        for tid, tobj in threading._active.items():
            if tobj is self:
                self._thread_id = tid
                return tid

        raise AssertionError("could not determine the thread's id")

    def raiseExc(self, exctype):
        _async_raise( self._get_my_tid(), exctype )

def work():
    while True:
        print('work')
        time.sleep(1)

class Server:
    def __init__(self):
        self.thread = ThreadWithExc(target=work)

    def start(self):
        self.thread.start()

    def stop(self):
        _async_raise(self.thread.raiseExc(TypeError))


server = Server()
server.start()
server.stop()

这会产生 ValueError: invalid thread id 异常。我还尝试了 threading.get_ident() 而不是答案的 _get_my_tid();这给了我另一个 ID,但那个 ID 也是无效的。

最佳答案

我认为您遇到的根本问题是您没有正确调用 _async_raise() ,应该替换该行:

_async_raise(self.thread.raiseExc(TypeError))

Server.stop()中:

self.thread.raiseExc(TypeError)

但是,如果您这样做,您将在线程 Thread-1 中得到一个异常:,因为 work() 函数中没有异常处理程序来处理由 raiseExc() 引发的异常。

以下内容修复了该问题并使用自定义 Exception 子类使事情变得更加清晰:

import threading
import inspect
import ctypes
import time

# https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python
def _async_raise(tid, exctype):
    if not inspect.isclass(exctype):
        raise TypeError("Only types can be raised (not instances)")
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
                                                  ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")

# https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python
class ThreadWithExc(threading.Thread):
    def _get_my_tid(self):

        if not self.isAlive():
            raise threading.ThreadError("the thread is not active")

        if hasattr(self, "_thread_id"):
            return self._thread_id

        for tid, tobj in threading._active.items():
            if tobj is self:
                self._thread_id = tid
                return tid

        raise AssertionError("could not determine the thread's id")

    def raiseExc(self, exctype):
        _async_raise(self._get_my_tid(), exctype )

def work():
    try:
        while True:
            print('work')
            time.sleep(1)
    except Server.ThreadStopped:
        pass

    print('exiting work() function')

class Server:
    class ThreadStopped(Exception): pass

    def __init__(self):
        self.thread = ThreadWithExc(target=work)

    def start(self):
        self.thread.start()

    def stop(self):
#        _async_raise(self.thread.raiseExc(TypeError))
        self.thread.raiseExc(self.ThreadStopped)

server = Server()
server.start()
server.stop()

输出:

work
exiting work() function

关于python - 停止线程 - 获取无效的线程 ID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44232664/

相关文章:

python - 使用滚动标准差检测 Pandas 数据框中的异常值

python - Pandas 被意外的分隔符搞糊涂了

java - 如何同步父类上嵌套的Java类方法?

Python 套接字端口使用

python - 无法在 Ubuntu Wily 上运行 Kivy

十六进制转储文件的pythonic方式

python-3.x - 在 tensorflow 中获取随机 Gamma 分布,如 numpy.random.gamma

python-3.x - Python 3套接字客户端未连接到服务器

c++ - 如何创建一系列线程以按启动顺序执行任务?

android - 为了在应用程序退出时终止工作线程,我们有什么钩子(Hook)