2010-12-31 3 views
4

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

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

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

// 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?

ответ

9

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

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

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

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

+0

Спасибо, ключ был не полностью загружен, когда поток выполнял LoadReports(). Я также удалил все ненужные вызовы InvokeRequired. – Zamboni