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

时间:2010-08-04 20:32:51

标签: python multithreading events

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

我无法找到一种方法来确保每个等待的线程在设置事件时恰好触发一次。我目前有触发线程设置它,睡了一会儿,然后清除它。不幸的是,这导致等待线程多次抓住设置事件,或者根本没有。

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

简而言之:在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()并挂起触发器,因此它无法清除事件。

4 个答案:

答案 0 :(得分:9)

您不需要事件,也不需要锁定和队列。您只需要一个队列。

致电queue.put即可停止发送消息,而无需等待其传递或处理。

在工作线程中调用queue.get以等待消息到达。

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对象。

答案 1 :(得分:3)

如果你想要每个线程可以按顺序处理的离散原子事件,那么就像krs1& bot403建议并使用队列。 Python Queue类是同步的 - 您不必担心锁定使用它。

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

答案 2 :(得分:2)

我过去使用的一个解决方案是用于线程间通信的Queue类。它是线程安全的,可以在使用多处理和线程库时用于线程之间的轻松通信。您可以让子线程等待某些东西进入队列,然后处理新条目。 Queue类还有一个get()方法,它接受一个方便的阻塞参数。

答案 3 :(得分:1)

我不是python程序员但是如果一个事件只能被处理一次,或许你需要切换到具有适当锁定的消息队列,这样当一个线程唤醒并收到事件消息时它将处理它并删除它来自队列,所以如果其他线程醒来并查看队列,它就不存在。