一尘不染

调用(委托)

c#

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

Invoke(Delegate):

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

任何人都可以解释一下这是什么意思(尤其是大胆的意思)吗?


阅读 225

收藏
2020-05-19

共1个答案

一尘不染

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

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

Control.InvokeRequired

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

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

  • 哪个动作优先?
  • 两者是否有可能同时发生?
  • GUI需要运行的所有其他命令会如何处理?

实际上,您正在中断队列,这可能会带来很多无法预料的后果。调用实际上是使您想要做的事情进入该队列的“礼貌”方式,并且此规则从.Net
2.0开始通过抛出的InvalidOperationException强制实施。

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

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

您可能会发现有用的其他阅读资料包括:

开始调用发生了什么

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

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

无效的跨线程操作

// 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之后,您可能希望考虑使用扩展方法来打包这些调用。堆栈溢出问题“
清理需要调用的乱码”中对此进行了详细介绍

对于历史上可能发生的事情也有进一步的记载。

2020-05-19