Отношения диспетчер-поток в WPF


Мне не совсем ясно, сколько диспетчеров есть в приложении и как они связаны с потоками или ссылаются на них.

Как я понимаю, приложение WPF имеет 2 потока (один для ввода, другой для пользовательского интерфейса) и 1 Диспетчер (связанный с UI-потоком). Что, если я создам другой поток-назовем его "рабочий поток" - при вызове Dispatcher.CurrentDispatcher в рабочем потоке, какой диспетчер я получу?

Другой случай: Предположим, консольное приложение с 2 потоками - основная нить, а входная-нить. В главном потоке я сначала создаю входной поток, а затем вызываю Application.Run()

Thread thread = new Thread(new ThreadStart(UserInputThreadFunction));
thread.Start();
Application.Run();

Там будет один диспетчер, верно? На входе-поток, делает диспетчер.CurrentDispatcher возвращает диспетчер основного потока? Или Как правильно передать экземпляр диспетчеру главного потока?

Может ли быть так, что в приложении WPF имеется более одного диспетчера? Есть ли в любом случае смысл создавать еще один диспетчер?

3 22

3 ответа:

Приложение WPF имеет 2 потока (один для ввода, другой для UI)

Это утверждение не совсем корректно. Приложение WPF имеет только один поток пользовательского интерфейса, который обрабатывает все взаимодействия пользовательского интерфейса и ввод данных пользователем. Существует также "скрытый" поток, отвечающий за рендеринг, но обычно разработчики не имеют с ним дела.

Отношение диспетчер / поток-один к одному, то есть один диспетчер всегда ассоциируется с одним потоком и может быть использован для отправки выполнения этому потоку. нитка. Dispatcher.CurrentDispatcher возвращает диспетчер для текущего потока, то есть при вызове Dispatcher.CurrentDispatcher в рабочем потоке вы получаете диспетчер для этого рабочего потока.

Диспетчеры создаются по требованию, что означает, что если вы обращаетесь к Dispatcher.CurrentDispatcher и нет диспетчера, связанного с текущим потоком, он будет создан. При этом число диспетчеров в приложении всегда меньше или равно числу потоков в приложении.

Приложение WPF по умолчанию имеет только один диспетчер. Диспетчер-это единственный поток, который позволит вам взаимодействовать с элементами пользовательского интерфейса. Он абстрагирует реализации от вас, так что вам нужно только беспокоиться о том, чтобы быть в потоке пользовательского интерфейса, то есть диспетчером.

Если вы пытаетесь напрямую взаимодействовать с визуальным элементом (например, установить текст на текстовое поле с помощью txtBkx.Text = "new") из рабочего потока, то вам придется переключиться на поток пользовательского интерфейса:

Application.Current.Dispatcher.Invoke(
    () => { txtBkx.Text = "new"; });

В качестве альтернативы вы можете использовать SynchronizationContext.Current (в то время как на пользовательском интерфейсе thread) и использовать его для выполнения делегатов в потоке пользовательского интерфейса из другого потока. Как вы должны заметить, что Dispatcher.CurrentDispatcher не всегда может быть установлен.

Теперь вы можете фактически создавать различные окна WPF в одном приложении и иметь отдельный диспетчер для каждого окна:

Thread thread = new Thread(() =>
{
    Window1 w = new Window1();
    w.Show();

    w.Closed += (sender2, e2) =>
    w.Dispatcher.InvokeShutdown();

    System.Windows.Threading.Dispatcher.Run();
});

thread.SetApartmentState(ApartmentState.STA);
thread.Start();  

В качестве дополнительного Примечания помните, что в MVVM вы можете обновить модель из потока без пользовательского интерфейса и вызвать события изменения свойств из потока без пользовательского интерфейса, поскольку WPF будет маршалировать события PropertyChanged для вас. Повышение CollectionChanged должен быть в потоке пользовательского интерфейса, хотя.

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

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