Відповідь на це запитання полягає у тому, як працюють елементи керування C #
Елементи керування у Windows Forms прив'язані до певного потоку та не є безпечними для потоків. Отже, якщо ви викликаєте метод управління з іншого потоку, ви повинні використовувати один із методів виклику елемента керування для маршалювання виклику відповідного потоку. За допомогою цієї властивості можна визначити, чи потрібно викликати метод виклику, що може бути корисним, якщо ви не знаєте, якому потоку належить елемент керування.
З Control.InvokeRequired
Фактично те, що Invoke робить, - це те, що код, який ви викликаєте, з’являється у потоці, на якому керування «живе», ефективно запобігаючи виняткам між потоками.
З історичної точки зору в .Net 1.1 це було насправді дозволено. Це означало, що ви можете спробувати виконати код у потоці "GUI" з будь-якого фонового потоку, і це в основному буде працювати. Іноді це може просто спричинити вихід вашої програми, оскільки ви фактично переривали графічний інтерфейс, коли він робив щось інше. Це виняток з перехресними потоками - уявіть, спробуйте оновити TextBox, поки графічний інтерфейс малює щось інше.
- Які дії мають пріоритет?
- Чи можливо взагалі обидва трапитися?
- Що відбувається з усіма іншими командами, необхідними для запуску графічного інтерфейсу?
Фактично ви перериваєте чергу, що може мати багато непередбачених наслідків. Invoke - це фактично "ввічливий" спосіб отримати те, що ви хочете зробити, до цієї черги, і це правило було застосовано з .Net 2.0 далі через викинутий InvalidOperationException .
Щоб зрозуміти, що насправді відбувається за лаштунками, і що мається на увазі під "GUI Thread", корисно зрозуміти, що таке 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 .
Існує також подальший запис того, що історично сталося, що може представляти інтерес.