Обновление пользовательского интерфейса из нескольких фоновых потоков - PullRequest
1 голос
/ 16 марта 2012

Проблема здесь немного абстрактна.Мы все знаем, для фонового потока, чтобы обновить некоторый элемент пользовательского интерфейса.

Dispatcher.Invoke()

- единственный вариант (так?).Однако Dispatcher.Invoke () сам по себе делегирует задачу обновления потоку пользовательского интерфейса.Рассмотрим сценарии, в которых:

  • Фоновый поток слишком часто обновляет пользовательский интерфейс.
  • Десятки потоков обновляют один и тот же пользовательский интерфейс.

Объект Dispatcher будет сохранятьпри делегировании задачи обновления потоку пользовательского интерфейса и потоку пользовательского интерфейса может работать медленно.Какое может быть возможное решение?Как мы могли решить такую ​​проблему в Windows Forms , где модель потоков была очень похожа на модель WPF?Предоставляет ли WPF какой-либо другой метод многопоточности?

regards,

Ответы [ 3 ]

2 голосов
/ 16 марта 2012

Если вы перегружаете очередь ввода потока GUI, публикуя сообщения (APC, делегаты и т. Д.) Быстрее, чем они могут быть обработаны, тогда будут проблемы, независимо от того, WPF / Forms / что угодно.

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

Если все данные должны отображаться без потерь (например, памятка GUI или средство визуализации HTML должно отображать все отправленное), тогда может потребоваться подходящее управление потоком через пулы объектов и / или отложенное размещение данных для предотвращения сообщения перегрузка очереди.

«Проблема здесь немного абстрактная», а не абстрактная. Я много раз сталкивался с этой проблемой в загруженных приложениях. В моем случае длительная перегрузка входной очереди имеет тенденцию приводить к взаимоблокировке, потому что все объекты из моего пула между потоками застревают в опубликованных сообщениях, которые не обрабатываются, и не передаются в пул, потому что графический интерфейс заблокирован - попытка получить объект из пустого пула: ((

1 голос
/ 16 марта 2012

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

  • Это нарушает жесткую связь между пользовательским интерфейсом и рабочим потоком, налагаемым Invoke.
  • Поток пользовательского интерфейса должен диктовать, когда элементы управления пользовательского интерфейса обновляются ... так и должно быть, когда вы действительно думаете об этом.
  • Нет риска переполнения очереди сообщений пользовательского интерфейса или отсутствия других сообщений, как в случае использования Invoke или BeginInvoke из рабочего потока.
  • Рабочий поток не должен ждать ответа от потока пользовательского интерфейса, как в случае с Invoke.
  • Вы получаете большую пропускную способность как в пользовательском интерфейсе, так и в рабочих потоках.
  • Invoke и BeginInvoke - дорогостоящие операции.

Я знаю, что все время говорю об этом, но во многих ситуациях действительно есть лучшие альтернативы Invoke. Методы маршалинга, такие как Invoke, way overused.

1 голос
/ 16 марта 2012

Прежде всего, почему необходимо обновлять пользовательский интерфейс, который часто не замечает человеческий глаз, в идеале, даже если какой-либо прогресс обновляется с промежутком в секунду, это приемлемо. Например, если вы пишете файл и, вероятно, пишете 4 Кбайт каждые 30 миллисекунд, человеческий глаз не заметит, и мы не заботимся о производительности на экране в миллисекундах.

Не только вы сделаете поток пользовательского интерфейса занятым, но Dispatcher.Invoke также заблокирует ваш другой поток до завершения выполнения, Dispatcher.BeginInvoke не заблокирует ваш другой поток.

Поток пользовательского интерфейса, вероятно, завершит обновления за несколько миллисекунд, если он просто обновляет несколько меток или прогрессирует на экране.

Ни WPF, ни какая-либо другая платформа не могут обеспечить лучший способ, потому что пользовательский интерфейс очень сложен, и разрешение доступа из нескольких потоков может привести к взаимным блокировкам, поэтому приложение может перестать отвечать на запросы. По этой причине каждая платформа, будь то java, target-c или любая инфраструктура пользовательского интерфейса, будет нуждаться в том, чтобы вы обновляли пользовательский интерфейс только в потоке его создателя.

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

...