I feel like I should know the answer to this, but I'm going to ask anyway just in case I'm making a potentially catastrophic mistake.
The following code executes as expected with no errors/exceptions:
static void Main(string[] args)
{
ManualResetEvent flag = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(s =>
{
flag.WaitOne();
Console.WriteLine("Work Item 1 Executed");
});
ThreadPool.QueueUserWorkItem(s =>
{
flag.WaitOne();
Console.WriteLine("Work Item 2 Executed");
});
Thread.Sleep(1000);
flag.Set();
flag.Close();
Console.WriteLine("Finished");
}
Of course, as is usually the case with multi-threaded code, a successful test does not prove that this is actually thread safe. The test also succeeds if I put Close
before Set
, even though the documentation clearly states that attempting to do anything after a Close
will result in undefined behaviour.
My question is, when I invoke the ManualResetEvent.Set
method, is it guaranteed to signal all waiting threads before returning control to the caller? In other words, assuming that I'm able to guarantee that there will be no further calls to WaitOne
, is it safe to close the handle here, or is it possible that under some circumstances this code would prevent some waiters from getting signaled or result in an ObjectDisposedException
?
The documentation only says that Set
puts it in a "signaled state" - it doesn't seem to make any claims about when waiters will actually get that signal, so I'd like to be sure.
Close
in the code before theThread.Sleep
, you get anObjectDisposedException
("Safe handle has been closed") during theWaitOne
. On the other hand, if you moveClose
directly after theThread.Sleep
, both threads get signaled and execution finishes successfully. So, undefined behaviour, there's a race condition in the "bad" code. I just want to make sure I don't have some similar undefined behaviour in what I think is my "good" code. :) – Read