首页 > 解决方案 > 线程 - 标记值或事件来打破循环

问题描述

我可以想到两种方法来打破 Python 线程中的循环,下面是最小的示例:

1 - 使用哨兵值

from threading import Thread, Event
from time import sleep

class SimpleClass():

    def do_something(self):
        while self.sentinel:
            sleep(1)
            print('loop completed')

    def start_thread(self):
        self.sentinel = True
        self.th = Thread(target=self.do_something)
        self.th.start()

    def stop_thread(self):
        self.sentinel = False
        self.th.join()

simpleinstance = SimpleClass()
simpleinstance.start_thread()
sleep(5)
simpleinstance.stop_thread()

2 - 使用事件

from threading import Thread, Event
from time import sleep

class SimpleThread(Thread):

    def __init__(self):
        super(SimpleThread, self).__init__()

        self.stoprequest = Event()

    def run(self):
        while not self.stoprequest.isSet():
            sleep(1)
            print('loop completed')

    def join(self, timeout=None):
        self.stoprequest.set()
        super(SimpleThread, self).join(timeout)

simpleinstance = SimpleThread()
simpleinstance.start()
sleep(5)
simpleinstance.join()

在 Python 文档中,它讨论了事件,但没有讨论更简单的“哨兵价值”方法(我在 Stack Overflow 上的许多线程答案中看到了这种方法)。

使用哨兵值有什么缺点吗?

具体来说,它是否会导致错误(我从来没有遇到过错误,但我想如果你试图在 while 循环中读取它的同一时刻更改哨兵的值,那么某些东西可能会中断(或者 CPython GIL 可能会保存我在这种情况下)。什么被认为是最好的(最安全的)做法?

标签: pythonmultithreadingpython-3.xthread-safetypython-multithreading

解决方案


如果您查看 的来源Event,您会发现您正在使用的函数对您没有任何价值:

class Event:
    def __init__(self):
        self._cond = Condition(Lock())
        self._flag = False

    def is_set(self):
        return self._flag

    def set(self):
        with self._cond:
            self._flag = True
            self._cond.notify_all() # No more-value, because you are not using Event.wait

因此,在您的情况下,它只是一个没有实际Event使用的哨兵值的精美包装器,这也会使您的操作时间减慢一点。

事件只有在你使用他们的方法时才有用wait


推荐阅读