Consider the following code:
using (var mre = new ManualResetEvent(false))
{
var bgWkr = new BackgroundWorker();
bgWkr.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var mrEvent = e.Argument as ManualResetEvent;
// some processing...
mrEvent.WaitOne();
// broadcast an event
};
bgWkr.RunWorkerAsync(mre);
// some other processing...
// hook into the same event
mre.Set();
}
Let's say that the spawned worker takes a bit of time to complete. We will have left the using block a while ago by the time the worker thread finishes and waits on the ManualResetEvent. I would assume that the mre would have been closed when leaving the using block (given that it would have been disposed) and this would throw an exception at the very least. Is this a safe assumption to make?
This example may not be the best one with the ManualResetEvent but it is to illustrate the case where we access an IDisposable object inside an anonymous method within a using block and the anonymous method is called after we have exited the using block. Is there some mechanism that keeps hold of the disposable object? I don't believe so but would like some confirmation as to why (if there is some sort of voodoo at work) or why not.
Cheers,
Yes, this code is wrong - the outcome is not really defined, but it would be quite reasonable for it to throw an exception at the
mrEvent.WaitOne()
, sincemrEvent
is the almost-certainly-now-disposedManualResetEvent
. Technically there's a chance that the worker thread was all ready to go, and the worker thread did its "some processing..." faster than the primary thread did the "some other processing...", but: I wouldn't rely on it. So in most cases:mrEvent
is dead already.As for how to avoid this: perhaps this simply isn't a scenario for
using
. But it occurs that since the worker thread does aWaitOne
, the worker thread'sWaitOne
cannot complete before the primary thread performs themre.Set()
call - so you could exploit that and move theusing
to the worker:Note, however, that this raises an interesting question of what happens if the primary thread throws an exception in the "some other processing..." - the call to
mre.Set()
would never be reached, and the worker thread would never exit. You might want to do themre.Set()
in afinally
: