Вот основные моменты:
- Нельзя управлять вызовами пользовательского интерфейса из потока, отличного от того, в котором они были созданы (поток формы).
- Вызовы делегатов (т. Е. Перехватчики событий) запускаются в том же потоке, что и объект, инициирующий событие.
Итак, если у вас есть отдельный поток «движка», выполняющий некоторую работу, и у вас есть некоторый пользовательский интерфейс, отслеживающий изменения состояния, которые могут отражаться в пользовательском интерфейсе (например, индикатор выполнения или что-то еще), у вас есть проблема. Пожар двигателя - это событие изменения объекта, которое было перехвачено формой. Но делегат обратного вызова, что Форма, зарегистрированная в движке, вызывается в потоке движка ... а не в потоке Формы. И поэтому вы не можете обновить какие-либо элементы управления из этого обратного вызова. Doh!
BeginInvoke приходит на помощь. Просто используйте эту простую модель кодирования во всех ваших методах обратного вызова, и вы можете быть уверены, что все будет хорошо:
private delegate void EventArgsDelegate(object sender, EventArgs ea);
void SomethingHappened(object sender, EventArgs ea)
{
//
// Make sure this callback is on the correct thread
//
if (this.InvokeRequired)
{
this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
return;
}
//
// Do something with the event such as update a control
//
textBox1.Text = "Something happened";
}
Это действительно очень просто.
- Используйте InvokeRequired , чтобы узнать, произошел ли этот обратный вызов в правильном потоке.
- Если нет, то повторно вызвать обратный вызов в правильном потоке с теми же параметрами. Вы можете повторно вызвать метод, используя методы Invoke (блокирование) или BeginInvoke (неблокирование).
- В следующий раз, когда вызывается функция, InvokeRequired возвращает false, потому что мы находимся в правильном потоке, и все довольны.
Это очень компактный способ решения этой проблемы и защиты ваших форм от многопоточных обратных вызовов событий.