The idiom I was using was this: I had a global ManualResetEvent called WakeUp; threads waiting on the condition would call WakeUp.WaitOne() (causing them to sleep until...); a thread identifying the waking condition would call WakeUp.Set(); WakeUp.Reset(). My reading of the MSDN documentation is that the call to Set() should wake all waiting threads, while the call to Reset() should reset the event handle for the next wake-up signal.
Tragically, this did not work and my program would randomly flounder. If I added a delay between the Set() and Reset() calls, the likelihood of floundering reduced, but wasn't eliminated -- and, besides, that is too disgraceful a hack to contemplate for more than a moment.
The right solution was to use a Monitor. The idiom here is as follows:
For Threads Waiting On Some Condition
Monitor.Enter(SyncObj);
while (!condition) { Monitor.Wait(SyncObj); }
Monitor.Exit(SyncObj);
which can be abbreviated to
lock (SyncObj) {
while (!condition) { Monitor.Wait(SyncObj); }
}
For Threads Establishing Some Condition
Monitor.Enter(SyncObj);
// Establish condition.
Monitor.PulseAll(SyncObj);
Monitor.Exit(SyncObj);
which can be abbreviated to
lock (SyncObj) {
// Establish condition.
Monitor.PulseAll(SyncObj);
}
Peace at last... (For the curious, in this particular application it would not have been sensible to set up a separate event handle for each condition, which is what one would normally do.)
No comments:
Post a Comment