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.