WPF BackgroundWorker против диспетчера - PullRequest
30 голосов
/ 21 сентября 2010

В моем приложении WPF мне нужно выполнить асинхронную операцию, а затем обновить GUI.И эту вещь мне приходится делать много раз в разные моменты с разными операциями.Я знаю два способа сделать это: Dispatcher и BackgroundWorker.

Потому что, когда я выберу, мне будет трудно вернуться, я спрашиваю вас: что лучше?Каковы причины выбора одного, а не другого?

Спасибо!Pileggi

Ответы [ 2 ]

37 голосов
/ 21 сентября 2010

Основное различие между Dispatcher и другими методами потоков заключается в том, что Dispatcher фактически не является многопоточным. Диспетчер управляет элементами управления, которым для правильной работы требуется один поток; метод BeginInvoke для Dispatcher ставит в очередь события для последующего выполнения (в зависимости от приоритета и т. д.), но все еще в том же потоке.

С другой стороны, BackgroundWorker фактически выполняет код в то же время, когда он вызывается, в отдельном потоке. Его также легче использовать, чем истинные потоки, поскольку он автоматически синхронизируется (по крайней мере, я думаю, что я правильно помню) с основным потоком приложения, который отвечает за элементы управления и очередь сообщений (поток Dispatcher в случае WPF и Silverlight), поэтому нет необходимости использовать Dispatcher.Invoke (или Control.Invoke в WinForms) при обновлении элементов управления из фонового потока, хотя это не всегда рекомендуется.

Как сказал Рид, Task Parallel Library - отличный альтернативный вариант.

Редактировать: дальнейшие наблюдения.

Как я сказал выше, Диспетчер на самом деле не многопоточный; это только создает иллюзию этого, потому что он запускает делегатов, которые вы передаете ему в другое время. Я бы использовал Dispatcher только тогда, когда код действительно имеет дело только с аспектом View приложения - то есть элементами управления, страницами, окнами и всем этим. И, конечно же, его основное использование - это на самом деле запуск действий из других потоков для корректного или правильного обновления элементов управления (например, установка фокуса только после того, как какой-либо элемент управления полностью отрисовал / разложил сам, легче всего выполнено с помощью Dispatcher, потому что в WPF рендеринг не совсем детерминирован).

BackgroundWorker может сделать многопоточный код намного проще, чем обычно; это простая концепция для понимания, и больше всего (если это имеет смысл) вы можете извлечь из нее пользовательских работников, которые могут быть специализированными классами, которые выполняют одну задачу асинхронно, со свойствами, которые могут функционировать как параметры, уведомления о ходе выполнения и отмены и т. д. Я всегда находил BackgroundWorker огромной помощью (кроме случаев, когда мне приходилось извлекать из него информацию, чтобы сохранить Культуру исходного потока для правильной локализации: P)

Самый мощный, но и сложный путь - использовать самый низкий доступный уровень, System.Threading.Thread; однако так легко ошибиться, что не рекомендуется. Многопоточное программирование - сложное , это само собой разумеющееся. Тем не менее, есть много полезной информации, если вы хотите понять все аспекты: эта прекрасная статья , написанная нашим молодым человеком Джоном Скитом, сразу же приходит на ум (последняя страница статьи также содержит большое количество очень интересные ссылки).

В .Net 4.0 у нас есть другая опция, Task Parallel Library. Я еще мало с этим работал, но из того, что я видел, это впечатляет (и PLINQ просто великолепен). Если у вас есть любопытство и ресурсы для его изучения, я бы порекомендовал это (вам не понадобится , чтобы многому научиться в конце концов).

8 голосов
/ 21 сентября 2010

BackgroundWorker хорош, если вы выполняете одну операцию, которая предоставляет уведомления о ходе выполнения и событие завершения. Однако, если вы собираетесь выполнять одну и ту же операцию несколько раз или несколько операций, вам потребуется более одного BackgroundWorker. В этом случае это может стать громоздким.

Если вам не нужны события прогресса, то использование ThreadPool и Dispatcher может быть проще - особенно если вы собираетесь выполнять довольно много разных операций.

Однако, если C # 4 является опцией, использование Task Parallel Library также является отличным вариантом. Это позволяет вам использовать настройку задач продолжения с использованием текущего SynchronizationContext, который во многих случаях предоставляет гораздо более простую, более чистую модель. Подробнее см. в моем блоге на эту тему.

...