Отношения диспетчер-поток в WPF
Мне не совсем ясно, сколько диспетчеров есть в приложении и как они связаны с потоками или ссылаются на них.
Как я понимаю, приложение WPF имеет 2 потока (один для ввода, другой для пользовательского интерфейса) и 1 Диспетчер (связанный с UI-потоком). Что, если я создам другой поток-назовем его "рабочий поток" - при вызовеDispatcher.CurrentDispatcher
в рабочем потоке, какой диспетчер я получу?
Другой случай:
Предположим, консольное приложение с 2 потоками - основная нить, а входная-нить. В главном потоке я сначала создаю входной поток, а затем вызываю Application.Run()
Thread thread = new Thread(new ThreadStart(UserInputThreadFunction));
thread.Start();
Application.Run();
Там будет один диспетчер, верно? На входе-поток, делает диспетчер.CurrentDispatcher возвращает диспетчер основного потока? Или Как правильно передать экземпляр диспетчеру главного потока?
Может ли быть так, что в приложении WPF имеется более одного диспетчера? Есть ли в любом случае смысл создавать еще один диспетчер?
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()
, чтобы обработать вызовы диспетчеру. Такой поток, как поток ввода консоли, будет недоступен для процесс вызывает.