4

python 模块threading有一个对象Thread用于在不同的线程中运行进程和函数。这个对象有start方法,但没有stop方法。我调用简单方法Thread时无法停止的原因是什么?stop我可以想象使用该join方法不方便的情况......

4

5 回答 5

10

start可以是通用的并且有意义,因为它只是触发线程的目标,但是通用的stop会做什么呢?根据您的线程正在做什么,您可能必须关闭网络连接、释放系统资源、转储文件和其他流,或任何数量的其他自定义、非平凡任务。任何可以以通用方式完成大部分这些事情的系统都会给每个线程增加如此多的开销,以至于它不值得,并且会非常复杂并且在特殊情况下会被击穿,几乎不可能工作和。您可以跟踪所有创建的线程,而无需join在主线程中调用它们,然后检查它们的运行状态并在主线程自行关闭时向它们传递某种终止消息。

于 2013-01-23T14:45:16.220 回答
7

绝对可以实现Thread.stop如下示例代码所示的方法:

import threading
import sys

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

################################################################################

import time

def main():
    test = Thread2(target=printer)
    test.start()
    time.sleep(1)
    test.stop()
    test.join()

def printer():
    while True:
        print(time.time() % 1)
        time.sleep(0.1)

if __name__ == '__main__':
    main()

该类Thread3的代码运行速度似乎比Thread2该类快大约 33%。


附录:

如果对 Python 的 C API 和模块的使用有足够的了解ctypes,就可以编写一种更有效的方法来在需要时停止线程。使用的问题sys.settrace是跟踪功能在每条指令之后运行。如果在需要中止的线程上引发异步异常,则不会导致执行速度损失。以下代码在这方面提供了一些灵活性:

#! /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 an 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)
于 2013-01-23T20:28:21.623 回答
5

以可靠的方式杀死线程并不是很容易。想想所需的清理工作:哪些锁(可能与其他线程共享!)应该自动释放?否则,你很容易陷入僵局!

更好的方法是自己实现适当的关闭,然后设置

mythread.shutdown = True
mythread.join()

停止线程。

当然你的线程应该做类似的事情

while not this.shutdown:
    continueDoingSomething()
releaseThreadSpecificLocksAndResources()

经常检查关机标志。或者,您可以依靠特定于操作系统的信号机制来中断线程,捕获中断,然后进行清理

清理是最重要的部分!

于 2013-01-23T14:40:34.107 回答
1

停止线程应该由程序员来实现。例如设计你的线程来检查它是否有任何要求它立即终止的请求。如果 python(或任何线程语言)允许您停止线程,那么您将拥有刚刚停止的代码。这很容易出错,等等。

想象一下,如果您的线程在您杀死/停止它时将输出写入文件。然后该文件可能未完成和损坏。但是,如果您简单地向您希望它停止的线程发出信号,那么它可以关闭文件,删除它等。您,程序员,决定如何处理它。Python 无法为您猜测。

我建议阅读多线程理论。一个不错的开始:http ://en.wikipedia.org/wiki/Multithreading_(software)#Multithreading

于 2013-01-23T14:41:49.577 回答
0

在某些平台上,您不能强制“停止”线程。这样做也很糟糕,因为那时线程将无法清理分配的资源。当线程正在做一些重要的事情时,它可能会发生,比如 I/O。

于 2013-01-23T14:40:24.677 回答