Подтвердить что ты не робот

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

4b9b3361

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