如何在Python中实现Thread.ResetAbort方法

时间:2019-05-14 14:48:29

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

在Python中实现了Thread.abort方法之后,我注意到Thread.reset_abort方法也可能有用。两者的灵感都来自C#中的Thread对象。尽管创建可以自动重新引发的异常尚待解决,但是取消计划的异常可能仍会有所帮助。

问题How to automatically re-raise exception after handlers已经被要求解决问题X,但是这个问题与问题Y有关。如果计划线程引发异常,则仍然可以取消它。但是,Python的API中没有PyThreadState_GetAsyncExc函数来查看是否设置了异常。

到目前为止,这里是在其他线程中引发异常的代码:

#! /usr/bin/env python3
import _thread
import ctypes as _ctypes
import threading as _threading

_PyThreadState_SetAsyncExc = _ctypes.pythonapi.PyThreadState_SetAsyncExc
# noinspection SpellCheckingInspection
_PyThreadState_SetAsyncExc.argtypes = _ctypes.c_ulong, _ctypes.py_object
_PyThreadState_SetAsyncExc.restype = _ctypes.c_int

# noinspection PyUnreachableCode
if __debug__:
    # noinspection PyShadowingBuiltins
    def _set_async_exc(id, exc):
        if not isinstance(id, int):
            raise TypeError(f'{id!r} not a int instance')
        if not isinstance(exc, type):
            raise TypeError(f'{exc!r} not a type instance')
        if not issubclass(exc, BaseException):
            raise SystemError(f'{exc!r} not a BaseException subclass')
        return _PyThreadState_SetAsyncExc(id, exc)
else:
    _set_async_exc = _PyThreadState_SetAsyncExc


# noinspection PyShadowingBuiltins
def set_async_exc(id, exc, *args):
    if args:
        class StateInfo(exc):
            def __init__(self):
                super().__init__(*args)

        return _set_async_exc(id, StateInfo)
    return _set_async_exc(id, exc)


def interrupt(ident=None):
    if ident is None:
        _thread.interrupt_main()
    else:
        set_async_exc(ident, KeyboardInterrupt)


# noinspection PyShadowingBuiltins
def exit(ident=None):
    if ident is None:
        _thread.exit()
    else:
        set_async_exc(ident, SystemExit)


class ThreadAbortException(SystemExit):
    pass


class Thread(_threading.Thread):
    def set_async_exc(self, exc, *args):
        return set_async_exc(self.ident, exc, *args)

    def interrupt(self):
        self.set_async_exc(KeyboardInterrupt)

    def exit(self):
        self.set_async_exc(SystemExit)

    def abort(self, *args):
        self.set_async_exc(ThreadAbortException, *args)

目标是取消线程上引发的异常。如果在处理完异常之后有可能自动重新引发自身,则能够取消其自动抛出功能也将有所帮助。目前,这些功能均不可用。

1 个答案:

答案 0 :(得分:0)

PyThreadState_SetAsyncExc函数具有特殊的功能,可用于取消其他线程上的异步引发异常。您只需要向exc参数传递一个特殊值即可。该文档说:

  

如果 exc 为NULL,则清除线程的挂起异常(如果有)。

尽管这与在已捕获并处理异常后取消自动引发自身异常的功能不同,但仍允许“已重置”已排定的异常。您只需要更改一些代码:

_NULL = _ctypes.py_object()


def _set_async_exc(id, exc):
    if not isinstance(id, int):
        raise TypeError(f'{id!r} not a int instance')
    if exc is not _NULL:
        if not isinstance(exc, type):
            raise TypeError(f'{exc!r} not a type instance')
        if not issubclass(exc, BaseException):
            raise SystemError(f'{exc!r} not a BaseException subclass')
    return _PyThreadState_SetAsyncExc(id, exc)


class Thread(_threading.Thread):
    def reset_abort(self):
        self.set_async_exc(_NULL)

更改和添加的代码实现了Thread.reset_abort方法,该方法可以取消已计划引发的任何异常(不仅仅是ThreadAbortException)。如果实现了可以自动引发自身的异常类,则它可能需要使用自己的reset_abort方法,该方法可以防止在调用后发生此类行为。尽管未实现C#提供的确切功能,但该解决方案可能提供了与当前使用Python可能实现的功能最接近的设置。