2011-05-02 92 views
4

我发现关于如何正确使用Dispatcher类的信息很少。如何在使用Dispatcher.Run()时避免竞争条件?

目前我使用它类似this question,但有一个固有的竞争条件,我没有看到任何地方提到。

假设你使用下面的代码来启动一个调度程序线程:

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

而稍后再尝试使用它:

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

这往往会抛出一个NullReferenceException作为Dispatcher.FromThread,通话可能由于不能保证Dispatcher.Run已被调用,因此返回null。

我已经做了适当的实现是使用信号来确保调度程序在继续在主线程中使用之前运行。

+1

比赛条件没有任何其他线程开始竞争条件不同,它听起来像你已经解决了你的问题。你有什么问题? – 2011-05-02 17:06:14

+0

是的 - 比赛条件没有任何其他比赛条件不同。我担心的是,它是固有的(我相信)你应该使用Dispatcher API,但是在MSDN的任何地方都没有评论它如何正确使用它。 – Travis 2011-05-03 02:52:44

+0

这不是Dispatcher的典型用法。通常,您的主线程上有一个Dispatcher(为您自动创建),就是这样。拥有多个UI线程的每个线程都有自己的Dispatcher是非常不寻常的。将调度程序用于非UI线程更是不寻常,这正是您在这里所做的。如果你远离这条蜿蜒曲折的道路,期望不得不破解丛林。 – 2011-05-03 12:23:40

回答

2

这是我最终做的,这是我相信你需要做的,以便正确使用Dispatcher。

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); 
    } 
} 

InitDispatcher返回后,您可以使用

Dispatcher.FromThread(executionThread).Invoke 

​​

元帅到调度线程调用。

3

这是一个较短的版本,作为效用函数完成,灵感来自yours,所以我忽略了评论。

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; 
    } 
}