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.
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;
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With