Объясните странное поведение диспетчера (скрытая функция WPF) - PullRequest
1 голос
/ 31 июля 2009

У меня есть окно с одной кнопкой внутри.

Кодовый код

private void Button_Click(object sender, RoutedEventArgs e)
{
    Trace.TraceInformation("Button ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

    Thread w = new Thread((ThreadStart) Worker);
    w.SetApartmentState(ApartmentState.STA);  // removing/adding this doesn't make effect
    w.Start();
    MessageBox.Show("Direct");
}

void Worker()
{
    Trace.TraceInformation("Worker ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

    this.Dispatcher.Invoke((Action)delegate
                               {
                                   Trace.TraceInformation("Invoked ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
                                   MessageBox.Show("Invoked");
                               });
}

Нажатие на кнопку приводит к появлению сообщений 2 .

В то же время, трассировка показывает те же цифры для Button ThreadId и Invoked ThreadId .

Ответы [ 3 ]

2 голосов
/ 31 июля 2009

Диспетчер всегда выполняет работу с потоком GUI. Вот почему ваш ThreadId совпадает. Вы спрашиваете о GUI-теме: "Какой у вас ThreadId?" а затем выполнить некоторую работу через Dispatcher, который снова отправляется в поток GUI.

1 голос
/ 01 августа 2009

После прогулки я понял, что происходит.

Вот мое объяснение идея о том, что не так с кодом (или причина, по которой вопрос был опубликован).

Button_Click выполняется в потоке Dispatcher. Поток Dispatcher, как я знаю, является единственным для окна и его дочерних элементов.

Даже если Button_Click занимает больше секунды (достаточно долго), и пользователю удается снова нажать кнопку (или каким-либо другим образом взаимодействовать с пользовательским интерфейсом), следующий Button_Click (или другой соответствующий обработчик) не выполняется немедленно , но помещается в очередь диспетчеризации .

Dispatcher.Invoke выполняет делегат в потоке пользовательского интерфейса. Invoke, я полагаю, отправляет сообщение в цикл делегата GetMessage() и блокирует вызывающий поток, пока сообщение не завершится.

Я ожидал, что делегат начнет выполнение только после выхода из Button_Click.

MessageBox.Show() - это блокирующий вызов. Следующая инструкция не будет выполнена, пока пользователь не нажмет «ОК».

На самом деле происходит то, что Диспетчер фактически различает разные окна и знает, что Button_Click вызвал модальное диалоговое окно, и поэтому любое взаимодействие с окном должно издавать звуковой сигнал, а окно сообщения должно мигать.

Но продолжает отправлять сообщения. В конце концов, именно поэтому все щелчки пользователей преобразуются в сообщения Button.Click, и окно сообщений закрывается.

Вот почему вызванный делегат выполняется до выхода Button_Click. Вызванный делегат разбивается на Button_Click.

P.S. Как видно из кода, делегат также вызывает MessageBox.Show(). Это приводит к новому окну сообщения , которое является модальным к предыдущему. Я заметил, что не могу нажать «Ok» в «Прямом» msgBox, прежде чем «Вызвать».

0 голосов
/ 31 июля 2009

Я новичок в этом материале WPF Dispatcher, так что опустите меня, если я совершенно не прав, но похоже, что ваш вызов this.Dispatcher на самом деле ваш диспетчер Window (поток пользовательского интерфейса). Таким образом, любой код будет выполняться потоком пользовательского интерфейса, а не потоком, созданным вашим приложением.

[Изменить]

Вот вывод консоли, полученный при выполнении кода выше

DispatcherQuestion.vshost.exe Information: 0 : Button ThreadId: 9    // UI Thread
DispatcherQuestion.vshost.exe Information: 0 : Worker ThreadId: 11   // Thread w
DispatcherQuestion.vshost.exe Information: 0 : Invoked ThreadId: 9   // UI Thread

ThreadID 9 - поток пользовательского интерфейса. Ваша кнопка и ваш вызов Invoke выполняются в соответствии с замыслом потока пользовательского интерфейса.

Сегодня я нашел эту статью на MSDN, которая действительно прояснила некоторые вещи о модели потоков / диспетчеров WPF.

...