none
WaitHandle synchronization issues, or how to avoid deadlocks ? RRS feed

  • Question

  • Sample Code:
    ManualResetEvent mre = new ManualResetEvent(false);
     
    ThreadPool.QueueUserWorkItem(o=>
     {
       Thread.Sleep(1000);
       mre.Close();
     }
    );
    mre.WaitOne();
     
    The above program will be stuck forever, since disposing the WaitHandle does not set it, therefore never frees the waiting thread,
    which is using WaitOne()  (or any other Wait for that matter).
     
    The question is, whether this is a flow in the behavior of the framework, or not.
    If not, then how should this case be handled safely ?

    Assume that I can understand the explanation on why a waitHandle being handle is stuck forever from the point of view of the CLR. Is there any way in which I am able to avoid such situations ? maybe by wrapping these events with a "watchdog" class ? Note that I cannot use the .Set() method in the dispose due to possible issues with disposing etc.

    Should I Set() the waithandle when disposing it, just in case someone is stuck on it ?
    Should I use any other design pattern?
    Friday, September 18, 2009 9:53 AM

Answers

  • Writing multithreaded code is hard. It will never be easy.

    Every few years, someone comes up with a "genius" idea that will magically fix all multithreading and synchronizing problems so that programmers will never have to bother with them again. Every single time, the idea fails. Whenever I feel narcissistic enough to be contemplating an automatic synchronization framework, I just tell myself: "Smarter people than you have spent decades on this without finding a solution."

    Two .NET examples spring to mind:
    1) MethodImplOptions.Synchronized. The docs have been revised to state "... the Synchronized flag is not recommended for public types... This might cause deadlocks or other synchronization problems." The very first Google hit on "MethodImplOptions.Synchronized" is an MSDN blog containing the illuminating text "this is one of those ones that goes into the “don't do that” bucket".
    2) The collection auto-synchronization scheme (ICollection.SyncRoot, ICollection.IsSynchronized, *.Synchronized()). The docs now state "Enumerating through a collection is intrinsically not a thread-safe procedure. Even when a collection is synchronized, other threads can still modify the collection..." Note that the 2.0 generic collections abandoned this whole system, not supporting the Synchronized method and always returning false for IsSynchronized.

    Now, that's not to say that multithreading won't always be as hard as it is now. There's a lot of promising work being done in purely functional systems, where everything is immutable (and thus intrinsically threadsafe) once it has been created. PLINQ is fascinating. I've written Nito.Async to help programmers with multithreading/synchronization issues, but it's still hard.

    Back to the original question: as Hans said, your code has a bug. It is not a flaw in the framework. The case cannot "be handled safely" automatically. If you're interested in advanced deadlock detection, check out this MSDN article: Avoiding and Detecting .NET Deadlocks. It only covers the "lock" case, but it can be extended to include events, mutexes, etc. with a considerable amount of work. ;)

    But that's just detection. Then there's the classic problem that there is no such thing as a "correct recovery" from a deadlock. :)

    I'm going to go now. My mind has been bent enough already this morning. :)

           -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    • Marked as answer by eryang Wednesday, September 23, 2009 2:11 AM
    Friday, September 18, 2009 1:26 PM

All replies

  • To be clear: it's a bug in your code.  You are disposing an object that is in use.  What happens next depends on the class implementation.  If you are lucky, you'd get an ObjectDisposedException.  No such luck in this case, the main thread is deeply nested into a Windows API call that is blocking the thread.  That thread is not executing any code which leaves few options to execute code to throw the exception.

    Well, that's not great and somewhat violates the promise of the CLR to always do the Right Thing.  You can post to connect.microsoft.com and suggest that they make it do the right thing.  Not so sure that's even possible, they'll tell you.

    Meanwhile, protect yourself against this kind of problem with defensive coding.  It is never ever appropriate to dispose a "thread completed" sync object in the thread itself.  Yes, you can implement a watchdog, WaitOne() takes a timeout argument.

    Hans Passant.
    Friday, September 18, 2009 10:41 AM
    Moderator
  • Writing multithreaded code is hard. It will never be easy.

    Every few years, someone comes up with a "genius" idea that will magically fix all multithreading and synchronizing problems so that programmers will never have to bother with them again. Every single time, the idea fails. Whenever I feel narcissistic enough to be contemplating an automatic synchronization framework, I just tell myself: "Smarter people than you have spent decades on this without finding a solution."

    Two .NET examples spring to mind:
    1) MethodImplOptions.Synchronized. The docs have been revised to state "... the Synchronized flag is not recommended for public types... This might cause deadlocks or other synchronization problems." The very first Google hit on "MethodImplOptions.Synchronized" is an MSDN blog containing the illuminating text "this is one of those ones that goes into the “don't do that” bucket".
    2) The collection auto-synchronization scheme (ICollection.SyncRoot, ICollection.IsSynchronized, *.Synchronized()). The docs now state "Enumerating through a collection is intrinsically not a thread-safe procedure. Even when a collection is synchronized, other threads can still modify the collection..." Note that the 2.0 generic collections abandoned this whole system, not supporting the Synchronized method and always returning false for IsSynchronized.

    Now, that's not to say that multithreading won't always be as hard as it is now. There's a lot of promising work being done in purely functional systems, where everything is immutable (and thus intrinsically threadsafe) once it has been created. PLINQ is fascinating. I've written Nito.Async to help programmers with multithreading/synchronization issues, but it's still hard.

    Back to the original question: as Hans said, your code has a bug. It is not a flaw in the framework. The case cannot "be handled safely" automatically. If you're interested in advanced deadlock detection, check out this MSDN article: Avoiding and Detecting .NET Deadlocks. It only covers the "lock" case, but it can be extended to include events, mutexes, etc. with a considerable amount of work. ;)

    But that's just detection. Then there's the classic problem that there is no such thing as a "correct recovery" from a deadlock. :)

    I'm going to go now. My mind has been bent enough already this morning. :)

           -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    • Marked as answer by eryang Wednesday, September 23, 2009 2:11 AM
    Friday, September 18, 2009 1:26 PM