Python threading.Event() - 确保所有等待的线程在event.set()上唤醒 [英] Python threading.Event() - Ensuring all waiting threads wake up on event.set()

查看:198
本文介绍了Python threading.Event() - 确保所有等待的线程在event.set()上唤醒的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些线程等待一个事件,执行一些动作,然后等待事件再次。另外一个线程会在适当的时候触发事件。



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



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

$ b $简而言之:在Python中,如何在线程中设置一个事件,并确保每个等待的线程在事件被清除之前完成一次?



更新:



我尝试使用锁和队列进行设置,但不起作用。这是我有的:

 #全局 - 用于同步线程
waitingOnEvent = Queue.Queue
MainEvent = threading.Event()
MainEvent.clear()#不确定这是必要的,但认为我会安全
mainLock = threading.Lock()

def waitCall ():
mainLock.acquire()
waitingOnEvent.put(等待)
mainLock.release()
MainEvent.wait()
waitingOnEvent.get( F $)
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
似乎只有一个事件。 wait()线程正在唤醒,而queue.join()正在工作。这给我一个等待线程唤醒,从队列中抓取并调用task_done(),并且单个get()/ task_done()以某种方式清空队列并允许join()。然后,触发线程完成join(),清除事件,从而防止其他等待线程通过。为什么队列只能在一个get / task_done调用之后注册为空/完成?



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

解决方案

你不需要一个事件,你不需要锁和队列。所有你需要的是一个队列。



调用 queue.put 放弃一条消息,而不用等待它交付或处理。



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

 导入线程
导入队列

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()
如果data =='shutdown' :
打印自我,'关闭'
返回
打印自我,'收到一条消息:',数据

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


def broad cast_event(data):
在active_queues中的q:
q.put(data)

t1 = Worker()
t2 = Worker()

broadcast_event(第一个事件)
broadcast_event(第二个事件)
broadcast_event(shutdown)

t1.stop()
t2.stop()

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


I have a number of threads which wait on an event, perform some action, then wait on the event again. Another thread will trigger the event when it's appropriate.

I can't figure out a way to ensure that each waiting thread triggers exactly once upon the event being set. I currently have the triggering thread set it, sleep for a bit, then clear it. Unfortunately, this leads to the waiting threads grabbing the set event many times, or none at all.

I can't simply have the triggering thread spawn the response threads to run them once because they're responses to requests made from elsewhere.

In short: In Python, how can I have a thread set an event and ensure each waiting thread acts on the event exactly once before it gets cleared?

Update:

I've tried setting it up using a lock and a queue, but it doesn't work. Here's what I have:

# 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

The first time, itemsinq properly reflects how many calls are waiting, but only the first waiting thread to make the call will make it through. From then on, itemsinq is always 1, and the waiting threads take turns; each time the trigger call happens, the next goes through.

Update 2 It appears as though only one of the event.wait() threads is waking up, and yet the queue.join() is working. This suggests to me that one waiting thread wakes up, grabs from the queue and calls task_done(), and that single get()/task_done() somehow empties the queue and allows the join(). The trigger thread then completes the join(), clears the event, and thus prevents the other waiting threads from going through. Why would the queue register as empty/finished after only one get/task_done call, though?

Only one seems to be waking up, even if I comment out the queue.get() and queue.task_done() and hang the trigger so it can't clear the event.

解决方案

You don't need an Event, and you don't need both a Lock and a Queue. All you need is a Queue.

Call queue.put to drop a message in without waiting for it to be delivered or processed.

Call queue.get in the worker thread to wait for a message to arrive.

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()

The messages don't have to be strings; they can be any Python object.

这篇关于Python threading.Event() - 确保所有等待的线程在event.set()上唤醒的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆