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