Ответ на этот вопрос заключается в том, как работают элементы управления C #.
Элементы управления в Windows Forms привязаны к определенному потоку и не являются потокобезопасными. Следовательно, если вы вызываете метод элемента управления из другого потока, вы должны использовать один из методов вызова элемента управления для маршалинга вызова в соответствующий поток. Это свойство можно использовать, чтобы определить, нужно ли вызывать метод invoke, что может быть полезно, если вы не знаете, какой поток владеет элементом управления.
Из Control. InvokeRequired
По сути, Invoke гарантирует, что вызываемый вами код выполняется в потоке, в котором «живет» элемент управления, эффективно предотвращая перекрестные исключения.
С исторической точки зрения в .Net 1.1 это действительно было разрешено. Это означало, что вы можете попробовать выполнить код в потоке «GUI» из любого фонового потока, и это в основном сработает. Иногда это просто приводило к завершению вашего приложения, потому что вы фактически прерывали поток графического интерфейса пользователя, пока он делал что-то еще. Это исключение Cross Threaded Exception - представьте, что вы пытаетесь обновить TextBox, пока графический интерфейс рисует что-то еще.
- Какое действие имеет приоритет?
- Возможно ли, чтобы и то и другое произошло одновременно?
- Что происходит со всеми другими командами, которые необходимо запустить графическому интерфейсу?
Фактически, вы прерываете очередь, что может иметь множество непредвиденных последствий. Invoke - это, по сути, «вежливый» способ поместить то, что вы хотите сделать в эту очередь, и это правило применялось начиная с .Net 2.0 и далее через брошенный InvalidOperationException.
Чтобы понять, что на самом деле происходит за кулисами и что подразумевается под «потоком графического интерфейса», полезно понять, что такое Message Pump или Message Loop.
Фактически, на этот вопрос уже есть ответ в вопросе «Что такое насос сообщений», и он рекомендуется. прочтите, чтобы понять реальный механизм, с которым вы связаны при взаимодействии с элементами управления.
Другое чтение, которое может оказаться полезным, включает:
Что случилось с Begin Invoke
Одно из основных правил программирования графического интерфейса Windows состоит в том, что только поток, создавший элемент управления, может получить доступ и / или изменить его содержимое (за исключением нескольких задокументированных исключений). Попробуйте сделать это из любого другого потока, и вы получите непредсказуемое поведение - от тупика до исключений и наполовину обновленного пользовательского интерфейса. Правильный способ обновить элемент управления из другого потока - отправить соответствующее сообщение в очередь сообщений приложения. Когда насос сообщений приступает к выполнению этого сообщения, элемент управления обновляется в том же потоке, который его создал (помните, насос сообщений работает в основном потоке).
и, для более подробного обзора кода с репрезентативной выборкой:
Недействительные межпоточные операции
public delegate void ControlStringConsumer(Control control, string text);
public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text});
} else {
control.Text=text;
}
}
Когда вы оцените InvokeRequired, вы можете рассмотреть возможность использования метода расширения для упаковки этих вызовов. Это подробно описано в вопросе о переполнении стека Требуется очистка кода, помеченного с помощью Invoke .
Существует также дополнительная история исторического развития, которая может представлять интерес.
05.02.2013