Вы наверное знаете, что нельзя просто так обращаться к графическим элементам управления интерфейса пользователя (GUI формы) из других потоков, таких как BackgroundWorker [2]. Это связано с безопасностью данных потоков, когда конкурентное обращение разными потоками к одним и тем же данным на запись может привести к непредсказуемому поведению программы.
Предотвращают такие неприятные ситуации специальные программные решения. В потоке BackgroundWorker, например, этот функционал инкапсулирован в вызове ReportProgress и обработчике ProgressChanged. Для обращения к элементам интерфейса управления пользователя (к экземпляру класса System.Windows.Forms.Control, и следовательно экземпляру формы окна программы System.Windows.Forms.Form как частности) из сторонних потоков как раз и нужны свойство InvokeRequired и метод Invoke класса Control.
Методы Invoke/BeginInvoke позволяют Вам перепоручить (делегировать, delegate) классу GUI вызов некоторых связанных с интерфейсом пользователя методов. Передаваемые в форму данные (такие как значения параметров) помещаются в некую очередь, поддерживаемую потоком GUI. Поток GUI берет из этой очереди элементы и выполняет реальные вызовы синхронно с другими вызовами обработки GUI. С помощью Invoke вызывающий сторонний поток (не поток GUI) переводится в состояние ожидания, пока вызов не будет завершен потоком GUI. BeginInvoke сделает возврат немедленно в момент реального вызова.
Итак, invoke-методы реализованы в классе Control (и также в экземпляре формы Form1 приложения), и полезно их использовать только тогда, когда Вы вызываете методы формы или обращаетесь к её свойствам (т. е. к классу Control) из потока, который не относится к потоку обработки графического интерфейса формы (GUI).
[InvokeRequired]
Чтобы было понятно, давайте разберемся подробнее. InvokeRequired делает проверку, в каком контексте работает текущий код. Проверка InvokeRequired нужна только тогда, когда вызов/обращение к форме происходит из стороннего потока, не относящегося к GUI.
InvokeRequired всегда вернет true, если это работает контекст чужого потока (не потока GUI), тогда необходимо вызывать Invoke со ссылкой на текущий выполняемый код (как это делается, будет показано на примере ниже). Если это не сделать, то произойдет ошибка времени выполнения:
Недопустимая операция в нескольких потоках: попытка доступа к элементу управления
'имя_элемента_GUI' не из того потока, в котором он был создан.
InvokeRequired всегда вернет false, если работает поток GUI, и в этом случае вызывать Invoke не требуется.
[Invoke]
Добавим ясности: разберем простой конкретный пример с компонентом USB HID (библиотека UsbLibrary), код которого работает в отдельном потоке. Экземпляр класса UsbHidPort myUSB сделан дочерним по отношению к классу формы приложения, и в его обработчике приема данных необходимо отображать принятые данные в ListBox формы. Обработчик приема данных UsbHidPort работает в отдельном потоке, не в потоке GUI формы, поэтому без invoke-функционала добавить данные в ListBox формы формы нельзя. Для потокобезопасной работы в обработчике приема данных UsbHidPort нужно добавить код наподобие следующего:
Обратите внимание, что вызов InvokeRequired и Invoke формы приложения происходит через переменную ParentForm, добавленную к классу myUSB, которая была предварительно инициализирована в коде загрузки формы после вызова конструктора экземпляра класса mySUB.
namespaceWindowsFormsApplication1
{
publicpartialclassFormMain : Form
{
log logfile;
public myUSB myusb;