Backgroundworker / Control.BeginInvoke () пользовательский интерфейс замораживания - PullRequest
2 голосов
/ 09 мая 2011

У меня есть код, который выполняет Windows SVC (другой процесс) и обновляет пользовательский интерфейс в то же время. В звонках используется BeginInvoke, вот так:

Install.BeginInvoke((MethodInvoker) delegate { Install.Enabled = false; });

Это в обработчике событий DoWork. Тем не менее, пользовательский интерфейс все еще зависает. Нужен ли мне звонок на Application.DoEvents где-нибудь (если да, то где?)? Как я могу устранить замораживание?

В нажатие кнопки, у меня есть:

GetPrinterDto(DirectoriesTreeView.SelectedNode.Text);

InstallBackgoundWorker.RunWorkerAsync();

InstallBackgroundWorker просто запускает код, который обновляет интерфейс и т. Д.

То, что я пытаюсь сделать, это вызвать сервер WCF (размещенный в службе Windows - это нормально), но пока это не работает, обновите индикатор выполнения и метку произвольными значениями асинхронно. Когда я выбираю триод, то отображается обработчик событий для выбранного триода, что может вызвать некоторое замедление. Я постараюсь поместить это в свой фоновый рабочий.

Ответы [ 2 ]

4 голосов
/ 09 мая 2011

Хотя вы, конечно, могли бы разместить вызов для обновления пользовательского интерфейса (что и делает ваш вызов BeginInvoke) в методе DoWork, вы не должны, в противном случае вы действительно не захотитене нужен класс BackgroundWorker.

Скорее, вы бы вызвали метод ReportProgress , который затем вызвал бы событие ProgressChanged .

Именно в обработчике события ProgressChanged вы могли бы сделать вызов Install.Enabled = false.

1 голос
/ 09 мая 2011

Проблема не связана напрямую с использованием BeginInvoke. Вероятно, это больше связано с тем, как это называется. Вы можете использовать ReportProgress в сочетании с событием ProgressChanged, но при условии, что вы замените все свои BeginInvoke вызовы на ReportProgress, вы можете столкнуться с подобными проблемами, потому что BackgroundWorker автоматически упорядочит обработчики событий в Поток пользовательского интерфейса использует те же механизмы, которые BeginInvoke / Invoke используют в любом случае. Я бы сказал, что быстрое решение состоит в том, чтобы уменьшить частоту, с которой вы пытаетесь обновить пользовательский интерфейс.

Лично я думаю, что использование BeginInvoke для обновления пользовательского интерфейса с прогрессом рабочего потока way злоупотребляет. И в этом отношении класс BackgroundWorker также поддерживает этот неоптимальный метод. Обычно лучше, чтобы рабочий поток периодически публиковал информацию об обновлении в общей структуре данных, а затем поток пользовательского интерфейса мог бы забрать ее по собственному расписанию (обычно путем опроса с использованием Timer). Это имеет несколько преимуществ:

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

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

...