Другие способы обновления пользовательского интерфейса из потока в C # - PullRequest
2 голосов
/ 22 марта 2012

Мое приложение сильно зависит от потоков, чтобы выполнять сложную обработку очень больших данных.Пользовательский интерфейс должен быть обновлен во время обработки.Я знаю и пытался использовать BackgroundWorker's OnProgressChanged и RunWorkerCompleted методы для обновления пользовательского интерфейса.Также используется Invoke метод потока пользовательского интерфейса для обновления.Кажется, все работает нормально на 32-битной и 64-битной ОС Win XP.В Win Vista и Win 7 (32 и 64-разрядных) приложение случайно зависает при обновлении интерфейса с помощью метода Invoke.

Меняется ли поведение Invoke на разных ОС Win?Каковы другие способы обновления пользовательского интерфейса из потока, кроме Invoke?

Спасибо

Ответы [ 4 ]

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

Не знаю, что происходит, но вы всегда можете запустить System.Windows.Forms.Timer, который периодически обновляет графический интерфейс; используйте некоторые переменные-члены для передачи необработанных данных между потоками, при необходимости внутри блокировок. Не самое элегантное решение, но оно может дать вам другое представление о том, что висит, так как потоки более независимы, чем полагаться на фоновый поток для Invoke вашего основного потока.

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

Изменяется ли поведение Invoke на разных ОС Win?

Не должно, нет. Но проблемы с потоками могут материализоваться очень непредсказуемым образом. Возможно, у вас есть неопознанная проблема.

Каковы другие способы обновления пользовательского интерфейса из потока, кроме Invoke?

Использование Invoke или BeginInvoke слишком часто используется, особенно при попытке сообщить простую информацию о ходе выполнения в поток пользовательского интерфейса. Если вы будете искать некоторые из моих ответов, относящихся к теме, вы увидите, что я постоянно использую этот подход. И на то есть веские причины, так как их использование сопряжено со многими недостатками. К сожалению, BackgroundWorker использует этот механизм исключительно для обновления пользовательского интерфейса через событие ProgressChanged.

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

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

Вы можете попробовать использовать одну из перегрузок Invoke () или BeginInvoke (), которая принимает перечисление Dispatcher.Priority в качестве параметра. Если вы выберете такой параметр, как «Фон», вы увидите, что ваше приложение все еще реагирует. Тогда единственной проблемой становится обеспечение того, что вы обрабатываете свои входящие данные с достаточной скоростью без постоянно увеличивающейся очереди.

0 голосов
/ 22 марта 2012

Другой вариант - полностью отказаться от многопоточности. Если ваша длительная операция может быть разбита на куски, сделайте это в потоке GUI и используйте Application.DoEvents(), когда вы хотите обновить GUI.

Раньше мне не нравилось использовать этот метод, поскольку он мог не только обновлять графический интерфейс, но и начинать реагировать на пользовательский ввод, запускать таймеры и т. Д., Но в конечном итоге это не менее безопасно, чем использование фонового потока, который позволяет графический интерфейс начать делать что-нибудь в любое время. Поэтому, вероятно, после каждого вызова Application.DoEvents() вам нужно будет проверить _canceled или что-то еще. (В конце концов я решил, что мне не нравится существование этого метода, поскольку он исключает гарантии линейного порядка выполнения, чем его использование).

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

...