Кто-нибудь может объяснить это заявление, написанное по этой ссылке
Invoke(Delegate):
Выполняет указанный делегат в потоке, которому принадлежит базовый дескриптор окна элемента управления.
Может ли кто-нибудь объяснить, что это значит (особенно жирный), я не могу это четко понять
Ответы:
Ответ на этот вопрос заключается в том, как работают элементы управления C #.
Из Control.InvokeRequired
Фактически, Invoke гарантирует, что вызываемый вами код выполняется в потоке, в котором «живет» элемент управления, эффективно предотвращая перекрестные исключения.
С исторической точки зрения в .Net 1.1 это действительно было разрешено. Это означало, что вы можете попробовать выполнить код в потоке «GUI» из любого фонового потока, и это в основном сработает. Иногда это просто приводило к завершению вашего приложения, потому что вы фактически прерывали поток графического интерфейса пользователя, пока он делал что-то еще. Это исключение Cross Threaded Exception - представьте, что вы пытаетесь обновить TextBox, пока графический интерфейс рисует что-то еще.
Фактически вы прерываете очередь, что может иметь множество непредвиденных последствий. Invoke - это, по сути, «вежливый» способ поместить то, что вы хотите сделать, в эту очередь, и это правило применялось начиная с .Net 2.0 и далее с помощью брошенного InvalidOperationException .
Чтобы понять, что на самом деле происходит за кулисами и что подразумевается под «потоком графического интерфейса», полезно понять, что такое Message Pump или Message Loop.
На самом деле на этот вопрос уже дан ответ в вопросе « Что такое Message Pump », и его рекомендуется прочитать для понимания реального механизма, с которым вы связаны при взаимодействии с элементами управления.
Другое чтение, которое может оказаться полезным, включает:
Что случилось с Begin Invoke
и, для более подробного обзора кода с репрезентативной выборкой:
Недействительные межпоточные операции
// the canonical form (C# consumer) public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type public void SetText(Control control, string text) { if (control.InvokeRequired) { control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself } else { control.Text=text; // the "functional part", executing only on the main thread } }
Когда вы оцените InvokeRequired, вы можете рассмотреть возможность использования метода расширения для упаковки этих вызовов. Это умело рассматривается в вопросе о переполнении стека. Очистка кода с пометкой Invoke Required .
Также может быть интересна дальнейшая запись исторического события .
источник
Элемент управления или объект окна в Windows Forms - это просто оболочка вокруг окна Win32, идентифицированного дескриптором (иногда называемым HWND). Большинство действий, которые вы делаете с элементом управления, в конечном итоге приведет к вызову Win32 API, который использует этот дескриптор. Дескриптор принадлежит потоку, который его создал (обычно главному потоку), и не должен управляться другим потоком. Если по какой-то причине вам нужно что-то сделать с элементом управления из другого потока, вы можете использовать,
Invoke
чтобы попросить основной поток сделать это от вашего имени.Например, если вы хотите изменить текст метки из рабочего потока, вы можете сделать что-то вроде этого:
theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
источник
this.Invoke(() => this.Enabled = true);
Все, о чемthis
идет речь, обязательно находится в текущей ветке, верно?Если вы хотите изменить элемент управления, это необходимо сделать в потоке, в котором этот элемент управления был создан. Этот
Invoke
метод позволяет выполнять методы в связанном потоке (потоке, которому принадлежит базовый дескриптор окна элемента управления).В приведенном ниже примере thread1 выдает исключение, потому что SetText1 пытается изменить textBox1.Text из другого потока. Но в thread2 действие в SetText2 выполняется в потоке, в котором был создан TextBox.
private void btn_Click(object sender, EvenetArgs e) { var thread1 = new Thread(SetText1); var thread2 = new Thread(SetText2); thread1.Start(); thread2.Start(); } private void SetText1() { textBox1.Text = "Test"; } private void SetText2() { textBox1.Invoke(new Action(() => textBox1.Text = "Test")); }
источник
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
источник
На практике это означает, что делегат гарантированно будет вызван в основном потоке. Это важно, потому что в случае элементов управления Windows, если вы не обновляете их свойства в основном потоке, вы либо не видите изменения, либо элемент управления вызывает исключение.
Шаблон такой:
void OnEvent(object sender, EventArgs e) { if (this.InvokeRequired) { this.Invoke(() => this.OnEvent(sender, e); return; } // do stuff (now you know you are on the main thread) }
источник
this.Invoke(delegate)
убедитесь, что вы вызываете делегата аргумент вthis.Invoke()
основном потоке / созданном потоке.Я могу сказать, что правило Thumb не имеет доступа к элементам управления вашей формы, кроме как из основного потока.
Возможно, следующие строки имеют смысл использовать Invoke ()
private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } }
Бывают ситуации, когда вы создаете поток Threadpool (т.е. рабочий поток), он будет выполняться в основном потоке. Он не будет создавать новый поток, потому что основной поток доступен для обработки дальнейших инструкций. Итак, сначала выясните, является ли текущий запущенный поток основным потоком, используя
this.InvokeRequired
if возвращает true, текущий код выполняется в рабочем потоке, поэтому вызовите this.Invoke (d, new object [] {text});иначе напрямую обновите элемент управления пользовательского интерфейса (здесь вы гарантированно запускаете код в основном потоке.)
источник
Это означает, что делегат будет работать в потоке пользовательского интерфейса, даже если вы вызываете этот метод из фонового рабочего или потока пула потоков. Элементы пользовательского интерфейса имеют сходство с потоками - им нравится разговаривать напрямую только с одним потоком: потоком пользовательского интерфейса. Поток пользовательского интерфейса определяется как поток, создавший экземпляр элемента управления, и поэтому связан с дескриптором окна. Но все это детали реализации.
Ключевой момент: вы должны вызвать этот метод из рабочего потока, чтобы вы могли получить доступ к пользовательскому интерфейсу (чтобы изменить значение в метке и т. Д.), Поскольку вам не разрешено делать это из любого другого потока, кроме потока пользовательского интерфейса.
источник
Делегат по сути является встроенным
Action
илиFunc<T>
. Вы можете объявить делегат вне области действия выполняемого вами метода или с помощьюlambda
выражения (=>
); поскольку вы запускаете делегат внутри метода, вы запускаете его в потоке, который выполняется для текущего окна / приложения, который выделен полужирным шрифтом.Пример лямбды
int AddFiveToNumber(int number) { var d = (int i => i + 5); d.Invoke(number); }
источник
Это означает, что переданный вами делегат выполняется в потоке, создавшем объект Control (который является потоком пользовательского интерфейса).
Вам необходимо вызвать этот метод, когда ваше приложение является многопоточным и вы хотите выполнить некоторую операцию пользовательского интерфейса из потока, отличного от потока пользовательского интерфейса, потому что если вы просто попытаетесь вызвать метод элемента управления из другого потока, вы получите System.InvalidOperationException.
источник