Multithreading: coding the register wait pattern
Yesterday we’ve looked at the theory behind registered waits (btw, there were some errors on that post, but I think I’ve corrected them now). Today, we’re going to look at some code. Let’s see what happens when we use a ManualResetEvent for waiting on:
class DumbHandle : WaitHandle { }
static void Main(string[] args) {
var evt = new ManualResetEvent(false);
var counter = 0;
var registerWait = ThreadPool.RegisterWaitForSingleObject(evt,
(state, timedout) => {
Interlocked.Increment(ref counter); //get number of queued calls
Console.WriteLine("registered wait fired");
},
null,
Timeout.Infinite,
false);
evt.Set();
Thread.Sleep(10);
evt.Reset();
var handle = new DumbHandle();
registerWait.Unregister(handle);
Console.WriteLine("total count: {0}", counter);
}
If you run the previous code, you’ll see that the sleep interval will be more than enough for firing the callback several times…notice that we’re passing a handle to the Unregister method so that all the callbacks have a chance of running before the program ends. If you don’t wish to do that, just pass null instead of a valid handle.
Since this is a really simple example, we’re not checking the values of the parameters passed to the callback method (in fact, in this example state will always be null and timeout will always be false because we’re specifying Timeout.Infinite for the timeout parameter on the RegisterWaitForSingleObject call).
You might be tempted to write code like this:
evt.Reset();
registerWait.Unregister(evt);
Console.WriteLine("total count: {0}", counter);
Please don’t! Doing this will result in getting more callbacks queued on the thread pool (which might run or not) and that’s not what you want!
I guess there isn’t much more to say about registered waits…keep tuned for more on multithreaded apps.