如何使用 Python 中的 Event 对象再次启动线程?

How to start a thread again with an Event object in Python?

我想创建一个线程并用一个事件对象来控制它。详细地说,我希望线程在设置事件对象时执行,并重复等待。

下面是我想到的粗略逻辑。

import threading
import time

e = threading.Event()

def start_operation():
    e.wait()

    while e.is_set():
        print('STARTING TASK')

        e.clear()



t1 = threading.Thread(target=start_operation)
t1.start()

e.set()    # first set
e.set()    # second set

我希望 t1 到 运行 一旦 the first set 被命令并停止自己(由于它里面有 e.clear),然后到 运行 在 the second set 被命令后再次。所以,按照我的预期,它应该打印出 'STARTING TASK' 两次。但它只显示一次,我不明白为什么。每当设置事件对象时,我应该如何更改代码以使其再次成为 运行 while loop

第一个问题是,一旦您退出 while 循环,您就已经退出了它。将谓词改回不会改变任何东西。暂时忘掉事件,看看这段代码:

i = 0
while i == 0:
    i = 1

后面再设置i = 0显然也无所谓吧?您已经离开了 while 循环和整个函数。而您的代码正在做完全相同的事情。

您可以通过添加另一个 while 循环来解决问题:

def start_operation():
    while True:
        e.wait()

        while e.is_set():
            print('STARTING TASK')

            e.clear()

然而,这仍然行不通——除非偶尔偶然发生。

Event.set 不阻塞;它只是立即设置事件,即使它已经设置。所以,这里最可能的控制流程是:

  • 后台线程命中 e.wait() 并阻塞。
  • 主线程命中 e.set() 并设置事件。
  • 主线程命中 e.set() 并再次设置事件,但没有任何效果。
  • 后台线程唤醒,循环一次,最后调用e.clear()
  • 后台线程永远等待 e.wait()

(没有办法避免错过事件信号的事实实际上是发明条件的原因,并且比 Win32 和 Python 更新的任何东西都不会打扰事件......但是条件不是'这里也不够。)


如果你想让主线程一直阻塞到事件清除,然后才重新设置,那是不行的。你需要一些额外的东西,比如第二个事件,主线程可以等待,后台线程可以设置。


但是如果你想跟踪多个 set 调用而不遗漏任何一个,你需要使用不同的同步机制。 queue.Queue 在这里可能有点矫枉过正,但在 Python 中却非常简单,所以我们就使用它吧。当然,您实际上没有任何值可以放入队列中,但这没关系;你可以在那里粘贴一个虚拟值:

import queue
import threading

q = queue.Queue()

def start_operation():
    while True:
        _ = q.get()
        print('STARTING TASK')

t1 = threading.Thread(target=start_operation)
t1.start()

q.put(None)
q.put(None)

如果您以后想添加一种关闭后台线程的方法,只需将其更改为坚持值:

import queue
import threading

q = queue.Queue()

def start_operation():
    while True:
        if q.get():
            return
        print('STARTING TASK')    

t1 = threading.Thread(target=start_operation)
t1.start()

q.put(False)
q.put(False)
q.put(True)