Как избежать состояния гонки при использовании Dispatcher.Run ()? - PullRequest
5 голосов
/ 02 мая 2011

Я нашел мало информации о том, как правильно использовать класс Dispatcher сам по себе.

В настоящее время я использую его, аналогично этому вопросу , но есть неотъемлемое условие гонки.о котором я нигде не упоминаю.

Предполагается, что вы используете следующий код для запуска потока диспетчера:

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

И попробуйте использовать его позже:

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

Это часто вызывает исключение NullReferenceException, так как вызов Dispatcher.FromThread может возвращать значение null, поскольку нет никакой гарантии, что Dispatcher.Run уже был вызван.

Что я сделал, чтобы правильно реализовать это, так это использовать сигналчтобы убедиться, что диспетчер работает, прежде чем продолжать использовать его в главном потоке.

Ответы [ 2 ]

4 голосов
/ 09 декабря 2011

Это более короткая версия, выполненная в виде служебной функции, вдохновленная вашим , поэтому я оставил комментарии.

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;
    }
}   
2 голосов
/ 03 мая 2011

Вот что я в итоге сделал, и я считаю, что вам нужно сделать, чтобы правильно использовать Диспетчер.

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

маршалировать вызовы в ветке диспетчера.

...