【问题标题】:Why does the python threading.Thread object has 'start', but not 'stop'? [duplicate]为什么 python threading.Thread 对象有“开始”,但没有“停止”? [复制]
【发布时间】:2013-01-07 01:56:21
【问题描述】:

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

【问题讨论】:

    标签: python multithreading


    【解决方案1】:

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

    【讨论】:

    • 后续问题:通常你可以通过按 CTRL-C 来停止 python 程序。在这种情况下,线程有什么区别?为什么不禁止使用 CTRL-C 停止程序,尽管您的所有论点仍然成立?
    • 这是一个系统事件,默认情况下会导致关闭程序的异常。正如@Anony-Mousse 在他的回答中所说,您必须捕获该事件并以自定义方式处理它才能正确清理您的线程。此外,如果您使用 CTRL-C 作为结束程序的标准方式,特别是如果您还没有以自定义方式处理事件,那么您只是在寻找错误。让系统杀死一个进程是只有在出现问题时才会真正发生的事情,或者当您正在执行诸如重新启动机器之类的操作时。
    【解决方案2】:

    绝对可以实现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)
    

    【讨论】:

      【解决方案3】:

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

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

      mythread.shutdown = True
      mythread.join()
      

      停止线程。

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

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

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

      清理是最重要的部分!

      【讨论】:

      • 如果你的线程中有asyncore.loop(),则不能这样做。
      • 这样做是有充分理由的。因为您确实想让线程停止并解锁它拥有的任何资源。否则,你迟早会陷入僵局。
      • 但是如何处理线程中运行的asyncore.loop()?如何很好地阻止它? (我想这是一个不同的问题......)
      • Alex:是的,这可能是一个单独的问题。看起来有人已经问过你了 :) stackoverflow.com/questions/10490077/…。尝试设置一个标志来指示循环应该关闭,并触发一个检查它的处理程序。这似乎是您问题的常见解决方案。我经常有一个单独的线程在某个队列上被阻塞,因此为了关闭它们,我将一个特殊值放入队列中,或者让它在每次获取后检查一个标志。
      • 哈,甚至在我问这个主要问题之前,我就发现了同样的问题。我现在正在尝试实施它。无论如何,谢谢。
      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-10-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多