在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)
目标是取消线程上引发的异常。如果在处理完异常之后有可能自动重新引发自身,则能够取消其自动抛出功能也将有所帮助。目前,这些功能均不可用。
答案 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可能实现的功能最接近的设置。