Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

WaitForMultipleObjects() Question

37 views
Skip to first unread message

Ron H

unread,
Nov 6, 2009, 8:45:05 AM11/6/09
to
I have an app with several worker threads that deal with external hardware.
Each thread has several independent tasks that are triggered by an array of
events. I use CreateEvent() to define the events
withWaitForMultipleObjects() being the gate keeper. And of course event 0 is
the shutdown task. At various points in the execution of the program, I use
SetEvent() to signal that I need one of the tasks to run. This is pretty
much straight out of Joe Newcomer's essays and it generally works well. Once
in a while an event is never acted upon and I believe that it is because the
thread gets bogged down and takes too long to complete a task... ( just a
guess)

Question: What is the life of an event? Is there a life timeout for a
signaled event? Is there a Queue for signaled events and if so how deep is
it?

Is there something I missed ( of course there is, but be gentle!)

Ron H.

Scott McPhillips [MVP]

unread,
Nov 6, 2009, 8:56:54 AM11/6/09
to
"Ron H" <rnh...@nospam.net> wrote in message
news:CLVIm.2988$ky1....@newsfe14.iad...


I've built a few similar designs and have not seen such a problem. I
suspect you have a race or overlap condition. I.e., if you set an event that
is already set then only one event will be detected, not two. To avoid this
situation I generally prefer to make the threads message driven, since that
gives you a queue of signals to the thread.

--
Scott McPhillips [VC++ MVP]

Alexander Grigoriev

unread,
Nov 6, 2009, 9:59:22 AM11/6/09
to
Make sure you create events with appropriate auto-reset property. For your
purposes, you most likely want AutoReset=TRUE.

"Ron H" <rnh...@nospam.net> wrote in message
news:CLVIm.2988$ky1....@newsfe14.iad...

Joseph M. Newcomer

unread,
Nov 6, 2009, 11:50:28 AM11/6/09
to
The lifetime of an event is from CreateEvent until the last CloseHandle (note that once an
event is created, you can create multiple handles, within the same process, within
different processes, by doing additional CreateEvent calls of a named event, by using
DuplicateHandle, or by inheritance). The lifetime of a given handle is from its creation
(by any of the aforesaid techniques) to the CloseHandle on that handle.

There is no timeout on a signaled event; there is only timeout on a WFSO/WFMO. Now one of
the potential problems of WFMO is "starvation", that is, if a lower-numbered handle is
often signaled, the higher-numbered handles will never be processed. Suppose you had a
handle H[2] which was signaled once every 100ms, and a handle H[3] which was signaled at
some other rate (more often or less often doesn't matter). Now suppose that to process
H[2] takes, on the average, 150ms. Then every time you return to the WFMO, H[2] is
signaled, and H[3], although it has been signaled since last Tuesday, will never be seen.

Note that there is no queue; there is no concept of queuing logic at all. Events are
handled in a strictly priority order, with the lowest-numbered event taking priority.

On solution we once applied to this problem was "round robin" scheduling of the WFMO. So
H[0] (the shutdown event) remained at location 0. But after each non-H[0] completion of
WFMO (or result > 1 and result < array element count), we did a "rotation" of the
elements; that is, element 1 was stored in a temp. The code was
HANDLE t = H[1];
for(int i = 1; i < count -1; i++)
H[i] = H[i] + 1;
H[count - 1] = t;

This was of course a bit more complex because we had to rotate the accompanying array of
elements that said what to do, but you get the idea. This guaranteed that eventually
every event became the "most important" event.

The problem was caused by the fact that every once in a while, a database query initiated
by a thread took a very long time. Once that happened, the world started falling apart
because that event was always signaled.

This was frankly a hack because there were something like 250K lines of source and the
original programmer had "just growed" the logic, and there was neither time nor budget to
rewrite the code to do something better.

Another way to handle this is to not use an event array at all, but instead use a queue of
elements. You can do this with mutex/CRITICAL_SECTION + Semaphore (see my essay on
semaphores) or what I am more likely to do, use an I/O Completion Port as my queue. Then
the threads, instead of doing a SetEvent, will do a PostQueuedCompletionStatus into the
I/O Completion Port and the receiving thread will do a GetQueuedCompletionStatus from that
port. This guarantees that events are always handled in FIFO order, and there is no
starvation. You can read my essay on I/O Completion Ports. To do a shutdown, you
PostQueuedCompletionStatus of a value or set of values that are "out of band", that is,
can never be legal; this is the way to send a notice to terminate the thread. Or, if a
thread terminates, the last thing it does is send a value or set of values OOB to indicate
thread status, such as thread completion (I sometimes have different sets of values to
indicate successful completion and error completion). Also, check out my technique for
having an OnIdle handler that guarantees you won't "flood" the main GUI thread message
pump with messages and delay everything, and note also you need a timer to guarantee that
this special queue gets handled in a timely fashion.
joe

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Joseph M. Newcomer

unread,
Nov 6, 2009, 11:51:04 AM11/6/09
to
Auto-reset doesn't help the "starvation" problem which is intrinsic in the priority
ordering of WFMO.
joe

Ron H

unread,
Nov 12, 2009, 6:47:13 AM11/12/09
to
Did you see that light come on? So if I signal event[4], then event[6], then
event[2], after event[4] completes, the next call toWaitForMultipleObjects()
will return event[2] before it returns event[6]???

Now I understand!!! Thanks Joe, I will go read your essay on I/O Completion
Ports...

Carry on!
Ron H.

Ron H

unread,
Nov 12, 2009, 9:51:51 AM11/12/09
to
Here's what I did:

I created a CRITICAL_SECTION FIFO, each set(int) stores the int in the next
available position in an array of ints and each Get() returns the oldest int
then shifts the array by 1. Then in the worker thread, the switch on
WaitForMultipleObjects() only gets either a 0 or 1 and on a 1 it gets the
next int in the FIFO and switches on that int to do the next operation.

Seems to work like a charm... Thanks!

Ron H.


0 new messages