Winform: многопоточное обновление пользовательского интерфейса одновременно - PullRequest
4 голосов
/ 31 декабря 2010

Я унаследовал некоторый код, который имеет два потока не-пользовательского интерфейса, которые обновляют различные элементы управления WinForm.
Код использует InvokeRequired и Invoke для обновления пользовательского интерфейса; однако время от времени я получаю сообщение об ошибке: Операция с несколькими потоками недопустима: доступ к элементу управления 'lvReports' осуществляется в потоке, отличном от того, в котором он был создан.

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

Итак, мой вопрос: как мне переписать приведенный ниже код для правильной обработки обновления пользовательского интерфейса с учетом состояния гонки и что мне нужно обновить пользовательский интерфейс из потоков, не относящихся к пользовательскому интерфейсу?

// two separate theads call this method in a instance of a WinForm
private void LoadReports()
{
  if (this.InvokeRequired)
  {
    this.Invoke(new MethodInvoker(this.LoadReports));
  }
  else
  {
    // some code removed to keep exampe simple...
    SetCtlVisible(lvReports, true);

    if (this.InvokeRequired)
    {
      this.Invoke((MethodInvoker)delegate { lvReports.Refresh(); });
    }
    else
    {
       lvReports.Refresh();
    }
  }
}

delegate void SetVisibleCallback(Control ctl, bool visible);
private void SetCtlVisible(Control ctl, bool visible)
{
  if (ctl.InvokeRequired)
  {
    SetVisibleCallback d = new SetVisibleCallback(SetCtlVisible);
    ctl.Invoke(d, new object[] { ctl, visible });
  }
  else
  {
    ctl.Visible = visible;
  }
}

Вот несколько мыслей: Отличается ли this.InvokeRequired от ctl.InvokeRequired в любое время? Нужен ли второй тест InvokeRequired с учетом первого? Нужна ли реализация SetCtlVisible, если я оставлю первый InvokeRequired? Должен ли я удалить первый InvokeRequired и оставить весь код в предложении else? Требуется ли блокировка вокруг условия else?

1 Ответ

9 голосов
/ 31 декабря 2010

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

Теперь вы можете использовать его для устранения вашей проблемы. Если это неверно, значит, что-то серьезно не так. Выкиньте исключение, отладчик остановится и позволит вам выяснить, почему он не работает должным образом. И всегда вызывайте Invoke (), вызывайте маленький вспомогательный метод, который выполняет остальную часть LoadReports ().

Также обратите внимание, что вы используете это неправильно в остальной части вашего кода. Вы знаете , что оставшаяся часть LoadReports () работает в потоке пользовательского интерфейса, вы использовали Invoke (). Нет смысла тестировать его снова, в том числе внутри SetCtlVisible ().

Типичная причина получения бомбы заключается в том, что поток запускает LoadReports () слишком рано, до создания окна формы. Вы должны заблокировать это. Событие Load формы является сигналом.

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