Search code examples
c#multithreadingwaithandle

Dispose WaitHandle for basic thread synchronization


According to documentation, WaitHandle in .NET should be explicitly/implicitly disposed. However, I'm having trouble achieving this for the following basic synchronization task:

  • a time consuming task is being executed on a thread.
  • the main thread waits for the task to complete for a predefined time-period. The main thread must proceed if a. the task is completed or b. the timeout occurred.

Here my attempt at using an AutoResetEvent object:

using(var waitHandle = new AutoResetEvent(false)){
    var worker = new Thread(() =>
    {
        try
        {
            TimeConsumingTask();
            waitHandle.Set(); //throws System.ObjectDisposedException: Safe handle has been closed
        }
        catch (Exception e)
        {...}
    }) {IsBackground = true};
    worker.Start(); //start worker

    const int waitTimeInMs = 5000; 
    var signaled = waitHandle.WaitOne(waitTimeInMs);//block main thread here. 
    if (!signaled)
    { //if timed out
       worker.Interrupt();
    }
}

There is an obvious race condition where the main thread wait times out and disposes the wait handle object which causes ObjectDisposedException exception. Is there any other way that I set this up so that the handle is properly disposed and without causing the exception?


Solution

  • Sure, there's no decent way to do this. Do note up front that you painted yourself into that corner by essentially leaving a thread running wild, nothing particularly nice about that.

    But you are focusing on the much smaller problem. The Thread class itself is already a resource hog, consuming a megabyte of VM and five synchronization objects. But it has no Dispose() method. This was courageous design, there's just no decent way to call the method.

    Disposing is optional, nothing that dramatic happens when you don't call it. The class has got your back, it has a finalizer that ensures that the native operating system resource will be released. Which will run, eventually, just not as quickly as you'd like.

    Compare this to a class with a less courageous design, the Task class has a Dispose() method. Which, like Thread, is almost as hard to call. The guidance from the .NET gurus is to just not bother.

    Same here.