C# 调用(委托)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14703698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 12:44:27  来源:igfitidea点击:

Invoke(Delegate)

c#winformsdelegatesinvoke

提问by user1903439

Can anybody please explain this statement written on this link

任何人都可以解释一下写在这个链接上的声明

Invoke(Delegate):

Executes the specified delegate on the thread that owns the control's underlying window handle.

在拥有控件的基础窗口句柄的线程上执行指定的委托

Can anybody explain what this means (especially the bold one) I am not able to get it clearly

任何人都可以解释这意味着什么(尤其是粗体)我无法清楚地理解

采纳答案by dash

The answer to this question lies in how C# Controls work

这个问题的答案在于 C# 控件的工作方式

Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control.

Windows 窗体中的控件绑定到特定线程并且不是线程安全的。因此,如果您从不同的线程调用控件的方法,则必须使用控件的调用方法之一将调用编组到正确的线程。此属性可用于确定是否必须调用 invoke 方法,如果您不知道哪个线程拥有控件,这会很有用。

From Control.InvokeRequired

来自Control.InvokeRequired

Effectively, what Invoke does is ensure that the code you are calling occurs on the thread that the control "lives on" effectively preventing cross threaded exceptions.

实际上,Invoke 所做的是确保您调用的代码发生在控件“存在”的线程上,从而有效地防止了跨线程异常。

From a historical perspective, in .Net 1.1, this was actually allowed. What it meant is that you could try and execute code on the "GUI" thread from any background thread and this would mostly work. Sometimes it would just cause your app to exit because you were effectively interrupting the GUI thread while it was doing something else. This is the Cross Threaded Exception- imagine trying to update a TextBox while the GUI is painting something else.

从历史的角度来看,在 .Net 1.1 中,这实际上是允许的。这意味着您可以尝试从任何后台线程在“GUI”线程上执行代码,这通常会起作用。有时它只会导致您的应用程序退出,因为您在执行其他操作时有效地中断了 GUI 线程。这是交叉线程异常- 想象一下在 GUI 绘制其他内容时尝试更新 TextBox。

  • Which action takes priority?
  • Is it even possible for both to happen at once?
  • What happens to all of the other commands the GUI needs to run?
  • 哪个动作优先?
  • 甚至有可能同时发生吗?
  • GUI 需要运行的所有其他命令会发生什么变化?

Effectively, you are interrupting a queue, which can have lots of unforeseen consequences. Invoke is effectively the "polite" way of getting what you want to do into that queue, and this rule was enforced from .Net 2.0 onward via a thrown InvalidOperationException.

实际上,您正在中断队列,这可能会产生许多无法预料的后果。Invoke 实际上是将您想做的事情放入该队列的“礼貌”方式,并且此规则从 .Net 2.0 开始通过抛出的InvalidOperationException强制执行。

To understand what is actually going on behind the scenes, and what is meant by "GUI Thread", it's useful to understand what a Message Pump or Message Loop is.

要了解幕后实际发生的事情以及“GUI 线程”的含义,了解什么是消息泵或消息循环很有用。

This is actually already answered in the question "What is a Message Pump" and is recommended reading for understanding the actual mechanism that you are tying into when interacting with controls.

这实际上已经在问题“什么是消息泵”中得到了回答,建议阅读以了解您在与控件交互时所使用的实际机制。

Other reading you may find useful includes:

您可能会觉得有用的其他阅读材料包括:

What's up with Begin Invoke

开始调用是怎么回事

One of the cardinal rules of Windows GUI programming is that only the thread that created a control can access and/or modify its contents (except for a few documented exceptions). Try doing it from any other thread and you'll get unpredictable behavior ranging from deadlock, to exceptions to a half updated UI. The right way then to update a control from another thread is to post an appropriate message to the application message queue. When the message pump gets around to executing that message, the control will get updated, on the same thread that created it (remember, the message pump runs on the main thread).

Windows GUI 编程的主要规则之一是只有创建控件的线程才能访问和/或修改其内容(除了少数记录的例外)。尝试从任何其他线程执行此操作,您将获得不可预测的行为,从死锁到异常,再到半更新的 UI。从另一个线程更新控件的正确方法是将适当的消息发布到应用程序消息队列。当消息泵开始执行该消息时,控件将在创建它的同一线程上更新(请记住,消息泵在主线程上运行)。

and, for a more code heavy overview with a representative sample:

并且,对于具有代表性示例的更多代码重概述:

Invalid Cross-thread Operations

无效的跨线程操作

// 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
    }
}

Once you have an appreciation for InvokeRequired, you may wish to consider using an extension method for wrapping these calls up. This is ably covered in the Stack Overflow question Cleaning Up Code Littered with Invoke Required.

一旦您对 InvokeRequired 有所了解,您可能希望考虑使用扩展方法来包装这些调用。这在 Stack Overflow 问题Cleaning Up Code Littered with Invoke Required 中有巧妙的介绍。

There is also a further write up of what happened historicallythat may be of interest.

还有一篇关于历史发生的事情的进一步记录,可能会引起人们的兴趣。

回答by Thomas Levesque

A control or window object in Windows Forms is just a wrapper around a Win32 window identified by a handle(sometimes called HWND). Most things you do with the control will eventually result in a Win32 API call that uses this handle. The handle is owned by the thread that created it (typically the main thread), and shouldn't be manipulated by another thread. If for some reason you need to do something with the control from another thread, you can use Invoketo ask the main thread to do it on your behalf.

Windows 窗体中的控件或窗口对象只是由句柄(有时称为 HWND)标识的 Win32 窗口的包装器。您对控件所做的大多数事情最终都会导致使用此句柄的 Win32 API 调用。句柄由创建它的线程(通常是主线程)拥有,不应由另一个线程操作。如果由于某种原因您需要对来自另一个线程的控件执行某些操作,您可以使用Invoke请求主线程代表您执行此操作。

For instance, if you want to change the text of a label from a worker thread, you can do something like this:

例如,如果您想从工作线程更改标签的文本,您可以执行以下操作:

theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));

回答by LukeHennerley

Delegate are essentially inline Action's or Func<T>. You can declare a delegate outside the scope of a method which you are running or using a lambdaexpression(=>); because you run the delegate within a method, you run it on the thread which is being run for the current window/application which is the bit in bold.

委托本质上是内联Action的 or Func<T>。你可以在你正在运行的方法的范围之外声明一个委托,或者使用lambda表达式(=>);因为您在方法中运行委托,所以您在为当前窗口/应用程序运行的线程上运行它,该线程以粗体显示。

Lambda example

Lambda 示例

int AddFiveToNumber(int number)
{
  var d = (int i => i + 5);
  d.Invoke(number);
}

回答by Marc Gravell

It means that the delegate will run on the UI thread, even if you call that method from a background worker or thread-pool thread. UI elements have thread affinity- they only like talking directly to one thread: the UI thread. The UI thread is defined asthe thread that created the control instance, and is therefore associated with the window handle. But all of that is an implementation detail.

这意味着委托将在 UI 线程上运行,即使您从后台工作线程或线程池线程调用该方法也是如此。UI 元素具有线程关联性——它们只喜欢直接与一个线程对话:UI 线程。UI 线程被定义为创建控件实例的线程,因此与窗口句柄相关联。但所有这些都是一个实现细节。

The key point is: you would call this method from a worker thread so that you can access the UI (to change the value in a label, etc) - since you are not allowedto do that from any other thread than the UI thread.

关键点是:您将从工作线程调用此方法,以便您可以访问 UI(以更改标签中的值等) - 因为除了 UI 线程之外,您不得从任何其他线程执行此操作。

回答by satnhak

In practical terms it means that the delegate is guaranteed to be invoked on the main thread. This is important because in the case of windows controls if you don't update their properties on the main thread then you either don't see the change, or the control raises an exception.

实际上,这意味着保证在主线程上调用委托。这很重要,因为在 Windows 控件的情况下,如果您不在主线程上更新它们的属性,那么您要么看不到更改,要么控件引发异常。

The pattern is:

图案是:

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)
}

回答by user1610015

It means that the delegate you pass is executed on the thread that created the Control object (which is the UI thread).

这意味着您传递的委托在创建 Control 对象的线程(即 UI 线程)上执行。

You need to call this method when your application is multi-threaded and you want do some UI operation from a thread other than the UI thread, because if you just try to call a method on a Control from a different thread you'll get a System.InvalidOperationException.

当您的应用程序是多线程的并且您想从 UI 线程以外的线程执行一些 UI 操作时,您需要调用此方法,因为如果您只是尝试从不同的线程调用 Control 上的方法,您将获得一个System.InvalidOperationException。

回答by Mehmet Ata?

If you want to modify a control it must be done in the thread in which the control was created. This Invokemethod allows you to execute methods in the associated thread (the thread that owns the control's underlying window handle).

如果要修改控件,则必须在创建控件的线程中完成。此Invoke方法允许您在关联线程(拥有控件的底层窗口句柄的线程)中执行方法。

In below sample thread1 throws an exception because SetText1 is trying to modify textBox1.Text from another thread. But in thread2, Action in SetText2 is executed in the thread in which the TextBox was created

在下面的示例中,线程 1 引发异常,因为 SetText1 试图从另一个线程修改 textBox1.Text。但是在thread2中,SetText2中的Action是在创建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"));
}

回答by Srikanth

this.Invoke(delegate)make sure that you are calling the delegate the argument to this.Invoke()on main thread/created thread.

this.Invoke(delegate)确保您this.Invoke()在主线程/创建的线程上调用委托参数。

I can say a Thumb rule don't access your form controls except from main thread.

我可以说除了主线程之外,Thumb 规则不会访问您的表单控件。

May be the following lines make sense for using Invoke()

可能以下几行对于使用 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;
        }
    }

There are situations though you create a Threadpool thread(i.e worker thread) it will run on main thread. It won't create a new thread coz main thread is available for processing further instructions. So First investigate whether the current running thread is main thread using this.InvokeRequiredif returns true the current code is running on worker thread so call this.Invoke(d, new object[] { text });

虽然您创建了一个线程池线程(即工作线程),但它会在主线程上运行。它不会创建新线程,因为主线程可用于处理进一步的指令。所以首先调查当前运行的线程是否是主线程使用this.InvokeRequiredif 返回 true 当前代码正在工作线程上运行所以调用 this.Invoke(d, new object[] { text });

else directly update the UI control(Here you are guaranteed that you are running the code on main thread.)

否则直接更新UI控件(这里保证你在主线程上运行代码。)

回答by BestDeveloper

Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });