5

I've found little information on how to properly use the Dispatcher class on its own.

Currently I am using it similar to this question, but there is an inherent race condition which I do not see mentioned anywhere.

Assuming you use the following code to start a dispatcher thread:

Thread thread = new Thread(Dispatcher.Run);
thread.Start();

And try to use it later:

Dispatcher.FromThread(thread).Invoke(MyMethodDelegate);

This will often throw a NullReferenceException as the Dispatcher.FromThread call may return null since there is no guarantee that Dispatcher.Run has been called yet.

What I've done to implement this properly is to use a signal to ensure the dispatcher is running before continuing to use it on the main thread.

Community
  • 1
  • 1
Travis
  • 2,654
  • 4
  • 26
  • 46
  • 1
    The race condition is no different than any other thread-start race condition, and it sounds like you've already solved your problem. What's your question? – Joe White May 02 '11 at 17:06
  • Yes - the race condition is no different than any other race condition. My concern is that it is inherent in the way (I believe) you are supposed to use the Dispatcher API, yet there is no comment on it anywhere on MSDN on how to use it appropriately. – Travis May 03 '11 at 02:52
  • That's not a typical use of Dispatcher. Typically you have a Dispatcher on your main thread (created for you automatically) and that's it. It's very unusual to have more than one UI thread each with its own Dispatcher. It's even more unusual to have Dispatchers for non-UI threads, which is what you appear to be doing here. If you go this far off the beaten track, expect to have to hack through the undergrowth. – Joe White May 03 '11 at 12:23
  • 1
    I am not using WPF (or Winforms, etc), so there is no UI thread involved. I had a need to marshal calls to a single thread (similar to what COM does). I'm using a 3rd party library in WCF and due to a bug in the library, I need all of these calls to the library to go through a single thread. My understanding was the Dispatcher class is essentially a wrapper around a windows message pump, created primarily for WPF. My point is that if you are going to use the Dispatcher class directly (it's in the framework, so its fair game), you absolutely need to know the above. – Travis May 03 '11 at 14:55

2 Answers2

4

This is a shorter version, done as a utility function, inspired by yours so I left out the comments.

private static Thread CreateDispatcherThread()
{
    using (var startedEvent = new ManualResetEventSlim()) 
    {
        var dispatcherThread = new Thread( _ => { 
            Dispatcher.CurrentDispatcher.BeginInvoke((Action)(startedEvent.Set));
            Dispatcher.Run(); } );
        dispatcherThread.Start();
        startedEvent.WaitHandle.WaitOne();
        return dispatcherThread;
    }
}   
Community
  • 1
  • 1
Tony Lee
  • 5,622
  • 1
  • 28
  • 45
2

Here is what I ended up doing, which is what I believe you need to do in order to use the Dispatcher properly.

private Thread executionThread;
private object SyncObject {get;set;}
private delegate void DispatcherMethod();

private void InitDispatcher()
{
    this.SyncObject = new object();

    // Set up the dispatcher pump.  See Dispatcher.Run on MSDN.
    this.executionThread = new Thread(StartDispatcher);

    lock (this.SyncObject)
    {
        this.executionThread.Start();
        Monitor.Wait(this.SyncObject);
    }
}   


private void StartDispatcher()
{
    DispatcherMethod method = DispatcherStarted;
    // Enqueue a started event by adding an initial method on the message pump.
    // Use BeginInvoke because the dispatcher is not actually running yet.
    // The call to Dispatcher.CurrentDispatcher handles creating the actual
    // Dispatcher instance for the thread (see MSDN - Dispatcher.FromThread
    // does not initialize the Dispatcher).
    Dispatcher.CurrentDispatcher.BeginInvoke(method);
    Dispatcher.Run();
}


private void DispatcherStarted()
{
    lock (this.SyncObject)
    {
        Monitor.Pulse(this.SyncObject);
    }
}

After InitDispatcher returns, you can use

Dispatcher.FromThread(executionThread).Invoke

or

Dispatcher.FromThread(executionThread).BeginInvoke

to marshal calls to the dispatcher thread.

Travis
  • 2,654
  • 4
  • 26
  • 46