Как уступить потоку пользовательского интерфейса для обновления пользовательского интерфейса при выполнении пакетной обработки в приложении WinForm? - PullRequest
9 голосов
/ 20 сентября 2008

У меня есть приложение WinForms, написанное на C # с .NET 3.5. Он запускает длительный пакетный процесс. Я хочу, чтобы приложение обновляло статус того, что делает пакетный процесс. Каков наилучший способ обновления пользовательского интерфейса?

Ответы [ 10 ]

14 голосов
/ 20 сентября 2008

BackgroundWorker звучит как нужный объект.

4 голосов
/ 20 сентября 2008

Быстрый и грязный способ использует Application.DoEvents() Но это может вызвать проблемы с обработкой событий заказа. Так что не рекомендуется

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

3 голосов
/ 11 января 2010

Чтобы узнать, что люди говорят о DoEvents, вот описание того, что может произойти.

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

К сожалению, экран не просто рисует, он также реагирует на действия пользователя. Это связано с тем, что DoEvents останавливает то, что вы делаете сейчас, для обработки всех сообщений Windows, ожидающих обработки вашим приложением Winforms. Эти сообщения включают в себя запросы на перерисовку, а также любой ввод текста, нажатие и т. Д.

Так, например, пока вы сохраняете данные, пользователь может сделать такие вещи, как заставить приложение отобразить модальное диалоговое окно, которое совершенно не связано с длительной задачей (например, Справка-> О программе). Теперь вы реагируете на новые действия пользователя внутри уже выполняющейся задачи, выполняющейся долго. DoEvents вернется, когда будут завершены все события, которые ожидали, когда вы его вызвали, и тогда ваше долгосрочное задание продолжится.

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

Здесь происходит то, что Application.DoEvents делает ваш код повторным. См. Определение википедии здесь . Обратите внимание на некоторые моменты в верхней части статьи, что для кода, подлежащего повторному вводу, это:

  • Не должно содержать статических (или глобальных) непостоянных данных.
  • Должен работать только с данными, предоставленными ему вызывающим абонентом.
  • Не должен полагаться на блокировки одноэлементных ресурсов.
  • Не должен вызывать не входящие компьютерные программы или процедуры.

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

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

3 голосов
/ 20 сентября 2008

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

Чтобы отобразить обновления, индикаторы выполнения или текст строки состояния являются двумя наиболее распространенными подходами.

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

1 голос
/ 22 ноября 2011

Используйте Backgroundworker, и если вы также пытаетесь обновить поток графического интерфейса, обрабатывая событие ProgressChanged (например, для ProgressBar), обязательно также установите WorkerReportsProgress=true или поток, который сообщение о прогрессе умрет в первый раз, когда он попытается позвонить ReportProgress ...

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

1 голос
/ 20 сентября 2008

Если вы работаете в фоновом / рабочем потоке, вы можете вызвать Control.Invoke на одном из ваших элементов управления пользовательского интерфейса, чтобы запустить делегата в потоке пользовательского интерфейса.

Control.Invoke является синхронным (ожидает возврата делегата). Если вы не хотите ждать, используйте .BeginInvoke(), чтобы поставить команду только в очередь.

Возвращаемое значение .BeginInvoke() позволяет проверить, завершен ли метод или дождаться его завершения.

1 голос
/ 20 сентября 2008

Я хочу повторить то, что отмечали мои предыдущие комментаторы: пожалуйста, по возможности избегайте DoEvents (), так как это почти всегда является формой «взлома» и вызывает кошмары обслуживания.

Если вы пойдете по пути BackgroundWorker (который я предлагаю), вам придется иметь дело с перекрестными вызовами пользовательского интерфейса, если вы хотите вызывать какие-либо методы или свойства элементов управления, так как они являются аффинно-зависимыми и должны быть вызывается только из той ветки, в которой они были созданы. При необходимости используйте Control.Invoke () и / или Control.BeginInvoke ().

1 голос
/ 20 сентября 2008

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

0 голосов
/ 20 сентября 2008

DoEvents () был тем, что я искал, но я также проголосовал за ответы фоновщика, потому что это похоже на хорошее решение, которое я исследую еще.

0 голосов
/ 20 сентября 2008

Application.DoEvents () или, возможно, запустить пакет в отдельном потоке?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...