Search code examples
c#timerevent-handlingmanualresetevent

ManualResetEvent.WaitOne stuck the GUI


I have a Timer that does several things

private System.Threading.Timer _xTimer = new System.Threading.Timer(new TimerCallback(XTimerHandler), null, 0, _xTimerPollingInterval);

private void XTimerHandler(object param)
{
    //some code.
}

I have a function which stops the timer:

private void Stop()
{
     //some code.
     if (_xTimer != null)
     {
        ManualResetEvent timerDisposeHandler = new ManualResetEvent(false);
        _xTimer.Dispose(timerDisposeHandler);
        _dataProcessingTimer = null;
        //wait for dispose end.
        timerDisposeHandler.WaitOne();

     }
}

Something very strange is going on!

Sometimes all the GUI hangs on timerDisposeHandler.WaitOne(); (but only sometimes, and I could not see a pattern that repeats itself where it happens, it just dynamically)

Has anyone encountered a similar problem and solved it?


Solution

  • Blocking the UI is effectively unsupported. While there are wait routines that are "supported" on the UI thread (which means they recognize there is a message pump and pump messages while you're waiting) it's not a good idea to do in general.

    First, you're blocking the UI. Yes, some message pumping can occur in certain circumstances, but do you really want the UI thread processing messages and waiting? Recipe for disaster if you're not really, really careful.

    Let's take a simple scenario. Let's say that you've done your due diligence when writing your Timer callback (because you've chosen System.Threading.Timer and not "System.Windows.Forms.Timer" which actually runs the Tick event handler on the UI thread) and have chosen to marshal data back to the UI thread with Control.Invoke (or Dispatcher.Invoke with WPF, you haven't specified what type of Windows application you're talking about). Conceptually you've got a Stop method that disposes the Timer and stops callbacks. The Dispose methods for Timer are documented as saying that the callback can be called after Dispose is called and thus a race condition, but also, there may be a callback being invoked at the same time you called Dispose. Both of these scenarios mean that between Dispose and WaitOne (or really just before WaitOne) your Timer callback may make that call to Invoke. Invoke is blocking and waits for the UI thread to process the message. But, if your Stop method was invoked by something on the UI (i.e. a message) that means the message pump is blocked on WaitOne. Deadlock.

    You could, possibly, fix this simply by switching out Invoke with BeginInvoke. But, you're still ultimately blocking the UI thread--keeping it, in most cases, from processing useful messages.

    Your question does not go into detail into what you're trying to do, so it's basically impossible to tell you exactly, or with any certainty, what to do to solve your problem. Short answer: redesign so you don't have to wait. If you're still stuck on that, I'd suggest asking a different question outlining what you want to accomplish, what you've tried, and what to do to do it better.