Отношения диспетчер-поток в 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(), чтобы обработать вызовы диспетчеру. Такой поток, как поток ввода консоли, будет недоступен для процесс вызывает.