WPF Dispatcher выполняет несколько путей выполнения - PullRequest
0 голосов
/ 03 мая 2010

Хорошо, я нашел что-то странное за выходные. У меня есть приложение WPF, которое порождает некоторые потоки для выполнения фоновой работы. Затем эти фоновые темы публикуют рабочие элементы в моем контексте синхронизации. Это все работает нормально, за исключением одного случая. Когда мои потоки заканчиваются, иногда они отправляют действия в диспетчер, который открывает всплывающее окно. В итоге происходит то, что если два потока публикуют действие в Dispatcher, оно начинает обрабатывать одно, а затем я открываю всплывающее окно с помощью Window.ShowDialog (); текущий путь выполнения приостанавливает ожидание обратной связи из диалогового окна, как и должно быть. Но проблема возникает в том, что когда диалоговое окно открывается, диспетчер затем запускается и запускается, сразу же запускается второе опубликованное действие. Это приводит к выполнению двух путей кода. Первый с открытым окном сообщения, а второй - с открытым, потому что состояние моего приложения неизвестно, потому что первое действие не было выполнено.

Я опубликовал пример кода, чтобы продемонстрировать поведение, о котором я говорю. Что должно произойти, так это то, что если я опубликую 2 действия и первое откроет диалоговое окно, второе действие не должно выполняться до тех пор, пока не будет выполнено 1-е действие.

public partial class Window1 : Window {

    private SynchronizationContext syncContext;
    public Window1() {
        InitializeComponent();
        syncContext = SynchronizationContext.Current;
    }

    private void Button_ClickWithout(object sender, RoutedEventArgs e) {
        // Post an action on the thread pool with the syncContext
        ThreadPool.QueueUserWorkItem(BackgroundCallback, syncContext);
    }

    private void BackgroundCallback(object data) {
        var currentContext = data as SynchronizationContext;

        System.Console.WriteLine("{1}: Thread {0} started", Thread.CurrentThread.ManagedThreadId, currentContext);

        // Simulate work being done
        Thread.Sleep(3000);

        currentContext.Post(UICallback, currentContext);

        System.Console.WriteLine("{1}: Thread {0} finished", Thread.CurrentThread.ManagedThreadId, currentContext);
    }

    private void UICallback(object data) {
        System.Console.WriteLine("{1}: UI Callback started on thread {0}", Thread.CurrentThread.ManagedThreadId, data);

        var popup = new Popup();

        var result = popup.ShowDialog();

        System.Console.WriteLine("{1}: UI Callback finished on thread {0}", Thread.CurrentThread.ManagedThreadId, data);
    }
}

XAML - это просто окно с кнопкой, которая вызывает Button_ClickWithout OnClick. Если дважды нажать кнопку и подождать 3 секунды, вы увидите, что два диалоговых окна всплывают один над другим, где первое всплывающее окно будет с ожидаемым поведением, а после его закрытия появится второе. *

Итак, мой вопрос: это ошибка? или как мне смягчить это, чтобы можно было обрабатывать только одно действие в то время, когда первое действие останавливает выполнение с помощью Window.ShowDialog ()?

Спасибо, Raul

Ответы [ 2 ]

0 голосов
/ 20 сентября 2011

Я знаю, что это старый вопрос, однако, поскольку я жду ответа на мой вопрос ( Советы по использованию приоритета и привязки диспетчера ), я подумал, что это будет платой вперед.

То, что вы испытываете, это Nested Pumping на Диспетчере. Я рекомендую прочитать статью MSDN о WPF Threading Model , особенно раздел под названием «Технические детали и точки преткновения», который находится на две трети вниз по странице. Подраздел, описывающий Nested Pumping , для удобства скопирован ниже.

Вложенная откачка

Иногда невозможно полностью заблокировать поток пользовательского интерфейса. Давайте рассмотрим метод Show класса MessageBox. Шоу не возвращаться, пока пользователь не нажмет кнопку ОК. Это, однако, создает окно, которое должно иметь цикл сообщений, чтобы быть интерактивным. В то время как мы ждем, когда пользователь нажмет OK, исходное приложение окно не отвечает на ввод пользователя. Тем не менее, он продолжает обрабатывать сообщения краски. Исходное окно перерисовывается, когда покрыты и раскрыты.

enter image description here

Некоторая ветка должна отвечать за окно сообщения. WPF мог создать новую ветку только для окна сообщения, но эту ветку не сможет нарисовать отключенные элементы в исходном окне (вспомните предыдущее обсуждение взаимного исключения). Вместо WPF использует систему обработки вложенных сообщений. Класс Dispatcher включает специальный метод, называемый PushFrame, который хранит Затем текущая точка выполнения начинает новый цикл обработки сообщений. Когда цикл вложенных сообщений завершается, выполнение возобновляется после исходного Вызов PushFrame.

В этом случае PushFrame поддерживает контекст программы при вызове MessageBox.Show, и он запускает новый цикл сообщений, чтобы перекрасить фоновое окно и дескриптор ввода в окно сообщения. Когда пользователь нажимает кнопку ОК и очищает всплывающее окно, выход из вложенного цикла и управление возобновляется после звонка на шоу.

0 голосов
/ 03 мая 2010

Модальное диалоговое окно не мешает окну владельца обрабатывать сообщения, в противном случае вы бы не смогли перерисовать его, когда модальное диалоговое окно было перемещено по его поверхности (просто в качестве примера).

Чтобы достичь того, чего вы хотите, вы должны реализовать свою собственную очередь в потоке пользовательского интерфейса, возможно, с некоторой синхронизацией, чтобы «разбудить ее» при поступлении первого рабочего элемента.

EDIT:

Также, если вы исследуете стек вызовов потока пользовательского интерфейса, когда 2-е модальное диалоговое окно открыто, вы можете обнаружить, что у него есть первый вызов ShowDialog над ним в стеке.

РЕДАКТИРОВАТЬ # 2:

Возможно, существует более простой способ сделать это, не создавая собственную очередь. Если вместо SynchronizationContext вы будете использовать объект Dispatcher, вы сможете вызвать BeginInvoke с приоритетом DispatcherPriority.Normal, и он будет правильно поставлен в очередь (проверка).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...