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

时间:2017-05-28 22:35:39

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

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

我试图实现与我的情况相对应的最简单的情况。我有一个产生线程的类,该线程应该在外部停止(线程永远不会自然完成,如本例所示)。 注意:/url#_async_raise是对此问题的接受答案的复制/粘贴

ThreadWithExc

这会产生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();这给了我另一个ID,但那个也无效。

2 个答案:

答案 0 :(得分:1)

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

_async_raise(self.thread.raiseExc(TypeError))
<{1>}中的

Server.stop()

但是,如果你这样做,你将获得self.thread.raiseExc(TypeError) ,因为Exception in thread Thread-1:函数中没有异常处理程序来处理由{{work()引发的异常。 1}}。

以下修复并使用自定义Exception子类使事情更加清晰:

raiseExc()

输出:

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

答案 1 :(得分:0)

Python 3.6:

self._thread_id = ctypes.c_long(tid)

Python 3.7:

self._thread_id = ctypes.c_ulong(tid)