Python threading.Event()-确保所有等待的线程在Event.set()上唤醒

Python threading.Event()-确保所有等待的线程在Event.set()上唤醒,python,multithreading,events,Python,Multithreading,Events,我有许多线程等待事件,执行一些操作,然后再次等待事件。另一个线程将在适当的时候触发该事件 我想不出一种方法来确保每个等待的线程在事件被设置时只触发一次。我目前有触发线程设置它,睡眠一点,然后清除它。不幸的是,这会导致等待线程多次抓取set事件,或者根本没有 我不能简单地让触发线程生成响应线程来运行它们一次,因为它们是对来自其他地方的请求的响应 简而言之:在Python中,如何让线程设置一个事件,并确保每个等待的线程在事件被清除之前只对该事件执行一次操作 更新: 我试着用锁和队列来设置它,但不起作

我有许多线程等待事件,执行一些操作,然后再次等待事件。另一个线程将在适当的时候触发该事件

我想不出一种方法来确保每个等待的线程在事件被设置时只触发一次。我目前有触发线程设置它,睡眠一点,然后清除它。不幸的是,这会导致等待线程多次抓取set事件,或者根本没有

我不能简单地让触发线程生成响应线程来运行它们一次,因为它们是对来自其他地方的请求的响应

简而言之:在Python中,如何让线程设置一个事件,并确保每个等待的线程在事件被清除之前只对该事件执行一次操作

更新:

我试着用锁和队列来设置它,但不起作用。以下是我所拥有的:

# Globals - used to synch threads
waitingOnEvent = Queue.Queue
MainEvent = threading.Event()
MainEvent.clear()    # Not sure this is necessary, but figured I'd be safe
mainLock = threading.Lock()

def waitCall():
    mainLock.acquire()
    waitingOnEvent.put("waiting")
    mainLock.release()
    MainEvent.wait()
    waitingOnEvent.get(False)
    waitingOnEvent.task_done()
    #do stuff
    return

def triggerCall():
    mainLock.acquire()
    itemsinq = waitingOnEvent.qsize()
    MainEvent.set()
    waitingOnEvent.join()
    MainEvent.clear()
    mainLock.release()
    return
第一次,itemsinq正确地反映了有多少调用正在等待,但只有第一个等待调用的线程才能通过。从那时起,itemsinq始终为1,等待的线程轮流执行;每次触发调用发生时,下一个都会通过

更新2 看起来似乎只有一个event.wait()线程正在唤醒,而queue.join()正在工作。这对我来说意味着一个等待的线程被唤醒,从队列中抓取并调用task_done(),而单个get()/task_done()以某种方式清空队列并允许join()。然后,触发器线程完成join(),清除事件,从而防止其他等待线程通过。但是,为什么只在一次get/task_done调用之后,队列就会注册为空/已完成


即使我注释掉queue.get()和queue.task_done(),似乎只有一个正在醒来挂起触发器,使其无法清除事件。

我不是python程序员,但如果一个事件只能处理一次,则可能需要切换到具有适当锁定的消息队列,以便当一个线程唤醒并接收到事件消息时,它将处理该事件并将其从队列中删除,这样其他线程就不会处理该事件唤醒并查看队列。

我过去使用的一个解决方案是用于线程间通信的类。它是线程安全的,当同时使用多处理库和线程库时,可以用来简化线程之间的通信。您可以让子线程等待某些内容进入队列,然后处理新条目。Queue类还有一个get()方法,该方法使用了一个方便的阻塞参数。

如果您希望离散的原子事件可以由每个线程顺序处理,那么按照krs1和bot403的建议执行并使用队列。Python
Queue
类是同步的-使用它不必担心锁定问题


但是,如果您的需求更简单(事件告诉您有可读取的数据等),您可以订阅/注册线程作为负责触发事件的对象的观察者。此对象将维护观测者
threading.Event
对象的列表。在触发器上,它可以对列表中的所有
threading.Event
对象调用set()。

您不需要事件,也不需要锁和队列。你只需要排队

调用
queue.put
,在不等待消息传递或处理的情况下将消息放入

调用
队列。在工作线程中获取
,以等待消息到达

import threading
import Queue

active_queues = []

class Worker(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.mailbox = Queue.Queue()
        active_queues.append(self.mailbox)

    def run(self):
        while True:
            data = self.mailbox.get()
            if data == 'shutdown':
                print self, 'shutting down'
                return
            print self, 'received a message:', data

    def stop(self):
        active_queues.remove(self.mailbox)
        self.mailbox.put("shutdown")
        self.join()


def broadcast_event(data):
    for q in active_queues:
        q.put(data)

t1 = Worker()
t2 = Worker()
t1.start()
t2.start()
broadcast_event("first event")
broadcast_event("second event")
broadcast_event("shutdown")

t1.stop()
t2.stop()

消息不必是字符串;它们可以是任何Python对象。

这听起来像是一个可能的设计缺陷,可能有更好的方法来实现您的目标。你能发布更多关于线程应该做什么的详细信息吗?这假设有一个静态的等待线程数,所有线程都是在开始时定义的,但事实并非如此。等待的线程是由请求生成的,因此数量是动态的和未知的。锁定的目的是防止其他等待线程添加自己、查看事件集、删除自己和通过-特别是因为从理论上讲,这可以防止队列永远清空。@culhantr好的,我重写了这个示例。