Отношения диспетчер с потоком в WPF - PullRequest
23 голосов
/ 16 февраля 2011

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

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

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

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

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

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

Ответы [ 3 ]

27 голосов
/ 16 февраля 2011

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

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

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

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

При этом число диспетчеров в приложении всегда меньше или равно количеству потоков в приложении.

11 голосов
/ 16 февраля 2011

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

Если вы пытаетесь напрямую взаимодействовать с визуалом (например, установить текст в текстовом поле с помощью 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, вы можете обновить модель из потока не из пользовательского интерфейса и вызвать события изменения свойств из потока без пользовательского интерфейса, так как WPF будет маршалировать события PropertyChanged для вас. Повышение CollectionChanged должно быть в потоке пользовательского интерфейса.

3 голосов
/ 16 февраля 2011

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

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

...