Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么python threading.Thread对象有';开始';,但不是';停止';?_Python_Multithreading - Fatal编程技术网

为什么python threading.Thread对象有';开始';,但不是';停止';?

为什么python threading.Thread对象有';开始';,但不是';停止';?,python,multithreading,Python,Multithreading,python模块有一个对象Thread,用于在不同的线程中运行进程和函数。此对象有一个start方法,但没有stop方法。调用简单的stop方法时,线程无法停止的原因是什么?我可以想象,在某些平台上,使用join方法很不方便……在某些情况下,您无法强制“停止”线程。这样做也不好,因为线程将无法清理分配的资源。当线程正在做一些重要的事情,比如I/O时,可能会发生这种情况。以可靠的方式杀死线程并不容易。想想所需的清理:哪些锁(可能与其他线程共享!)应该自动释放?否则,您将很容易陷入死锁 更好的方法是

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

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

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

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

mythread.shutdown = True
mythread.join()
停止线程

当然,您的线程应该执行以下操作

while not this.shutdown:
    continueDoingSomething()
releaseThreadSpecificLocksAndResources()
要经常检查关机标志。或者,您可以依赖操作系统特定的信号机制来中断线程、捕获中断,然后进行清理


清理是最重要的部分

停止线程应该由程序员来实现。例如,设计线程以检查是否存在任何立即终止线程的请求。如果python(或任何线程语言)允许您只停止一个线程,那么您的代码就会停止。这是容易出现错误的,等等

想象一下,当您终止/停止线程时,它正在将输出写入文件。那么文件可能未完成且已损坏。然而,如果你简单地通知线程你希望它停止,那么它可以关闭文件,删除它,等等。你,程序员,决定如何处理它。你猜不到


我建议你读一下多线程理论。一个好的开始:

start
可以是泛型的,因为它只是触发线程的目标,但是泛型的
停止
会做什么呢?根据线程正在执行的操作,您可能必须关闭网络连接、释放系统资源、转储文件和其他流,或者执行任意数量的其他自定义、非琐碎任务。任何能够以通用方式完成这些事情的系统都会给每个线程增加太多的开销,这是不值得的,而且会非常复杂,并且会遇到特殊情况,几乎无法使用。您可以跟踪所有创建的线程,而无需将它们加入主线程,然后检查它们的运行状态,并在主线程关闭时向它们传递某种终止消息。

当然可以实现
线程。停止
方法,如以下示例代码所示:

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)

如果线程中正在运行
asyncore.loop()
,则无法执行此操作。有充分的理由这样做。因为您确实希望线程停止并解锁它拥有的任何资源。否则,您可能迟早会遇到死锁。但是如何处理线程中运行的
asyncore.loop()
?如何很好地阻止这种情况?(我想这是一个不同的问题……)亚历克斯:是的,这可能是一个单独的问题。看起来有人已经帮你问过了:)。尝试设置一个标志来指示循环应该关闭,并触发一个检查循环的处理程序。这似乎是解决您问题的常见方法。我经常有一个单独的线程在某个队列上被阻塞,所以要关闭它们,我在队列中放入一个特殊值,或者让它在每次get后检查一个标志。哈,我甚至在问这个主要问题之前就发现了同样的问题。我现在正在努力实施它。无论如何,谢谢。下面的问题:通常你可以通过按CTRL-C来停止python程序。在这种情况下,线程有什么区别?为什么不禁止使用CTRL-C停止程序,尽管所有参数仍然有效?这是一个系统事件,默认情况下会导致关闭程序的异常。正如@anonymousse在他的回答中所说的,您必须捕获该事件并以自定义方式处理它,以便正确清理线程。此外,如果您使用CTRL-C作为结束程序的标准方式,特别是如果您没有以自定义方式处理事件,那么您只是在询问bug。只有当出现问题时,或者当您正在重新启动机器时,才应该让系统终止进程。