停止线程:标志与事件

Stop a thread: flag vs. Event

我看到示例 e.g. here of using an Event 停止线程,我认为布尔标志可以完成这项工作。

活动

class MyThread(threading.Thread):

    def __init__(self):
        self._please_stop = threading.Event()

    def run(self):
        while not self._please_stop.is_set():
        [...]

    def stop(self):
        self._please_stop.set()

旗帜

class MyThread(threading.Thread):

    def __init__(self):
        self._please_stop = False

    def run(self):
        while not self._please_stop:
        [...]

    def stop(self):
        self._please_stop = True

在这里使用 Event 有什么好处?它的 wait 方法没有被使用。是什么让它比布尔标志更好?

如果多个线程共享相同的 Event,我可以理解这一点,否则,我不明白。

This mailing list thread 建议 Event 会更安全,但我不清楚为什么。

更准确地说,我不明白那两段:

If I understand the GIL correctly, it synchronizes all access to Python data structures (such as my boolean 'terminated' flag). If that is the case, why bother using threading.Event for this purpose?

The GIL is an implementation detail and relying on it to synchronize things for you isn't futureproof. You're likely to have lots of warning, but using threading.Event() isn't any harder, and it's more correct and safer in the long term.

我同意使用 Event 几乎不会增加任何开销,因此我可以坚持这一点,但我想了解标志方法的局限性。

(我使用的是 Python3,所以我不担心 Python2 的限制,如果有的话,尽管这些在此处完全值得一提。)

我认为您引用的线程中的含义是设置布尔值不一定是 Python 中的 atomic 操作。虽然对所有 Python 对象(GIL)进行全局锁定使得所有设置属性 的操作暂时成为 原子,但将来可能不存在这样的锁定。使用 Event 使操作成为原子操作,因为它使用自己的锁进行访问。

atomic 的 link 是一个 Java 问题,但它的相关性并没有因此降低。

编程通常不仅仅是让代码在今天运行,而是让它在将来进行更改时保持运行。

  • 其他 Python 实现没有 GIL。我想 运行 明天 pypy 吗?
  • 实际上,我需要将工作分散到多个进程中。交换 multiprocessing... 实现 Event() 但如果您只是使用局部变量将会失败。
  • 事实证明,只有当其他几个线程认为应该停止时,代码才应该停止。好吧,使用 Semaphore() 而不是 Event()... 但是使用变量很容易错误地实现。

因此,根据字节码如何中断以及何时可以释放 GIL,您可能可以在 Python 中完美地正确编写多线程程序...但是如果我稍后阅读和更改您的代码,如果您使用标准同步原语,我会更高兴。