如何在使用Dispatcher.Run()时避免竞争条件?

本文关键字:竞争 条件 Run Dispatcher | 更新日期: 2023-09-27 17:54:06

我发现关于如何正确使用Dispatcher类的信息很少。

目前我正在使用类似于这个问题,但有一个固有的竞争条件,我没有看到任何地方提到。

假设您使用以下代码来启动调度程序线程:

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

并尝试以后使用它:

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

这通常会抛出NullReferenceException作为Dispatcher。FromThread调用可能返回null,因为不能保证Dispatcher。

为了正确地实现这一点,我所做的是在主线程上继续使用dispatcher之前使用一个信号来确保它正在运行。

如何在使用Dispatcher.Run()时避免竞争条件?

这是一个较短的版本,作为一个实用程序函数,受你的启发,所以我省略了评论。

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

这是我最终做的,我认为为了正确使用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

Dispatcher.FromThread(executionThread).BeginInvoke