Invoke和BeginInvoke的详细理解(C#)
在项目中,当使用Winform
(基于.NET Framework)构建程序的GUI界面时,若以TextBox
为列,要在其它线程中更新控件的界面显示,初学者往往是使用:
this.textBox.Text = "10";
当这样使用时,在程序员运行后可能会使程序触发“不能从不是创建该控件的线程中调用它”的异常信息。造成这种异常的原因在于,窗体控件是在主线程中创建的(比如this.Controls.Add(控件的组件对象)
),当更新控件的界面显示时,是在其它线程,并不是在主线程中进行。当在其它线程中更新控件的显示界面时,可能会与主线程发生线程冲突。若此时主线程正在重绘此控件,而其它线程也在更新此控件的显示界面,其它线程就会和主线程进行竞争,造成画面混乱,甚至可能会出现死锁。
因此在Windows GUI
编程中有一个规则,只能通过创建控件的线程来操作控件的数据,否则会产生不可预料的后果。
因此在.NET
中,为了更好的解决这些问题,通过Control
类实现了ISynchronizeInvoke
接口,提供了Invoke
和BeginInvoke
方法,提供让其它线程对GUI界面的操作。ISynchronizeInvoke
接口详细信息如下所示:
public interface ISynchronizeInvoke
{[HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)]IAsyncResult BeginInvoke(Delegate method, object[] args);object EndInvoke(IAsyncResult result);object Invoke(Delegate method, object[] args);bool InvokeRequired { get; }
}
如果从创建控件的线程外操作GUI
控件,就需要使用ISynchronizeInvoke
接口中的Invoke
(同步执行)和BeginInvoke
(异步执行)方法,通过一个委托把调用封送到控件所属的线程上执行。
通过上面的介绍,我们不得不思考一个问题,为什么Control
类提供了Invoke
和BeginInvoke
方法?
关于这个问题,我主要从以下几个方面来进行讲解:
1.Windows
程序消息机制
在Windows
中,GUI程序是基于消息机制,有一个主线程维护着一个消息泵。这个主线程维护着整个窗体以及窗体上面的子控件。当它获取到消息时,就会调用DispatchMessage()
方法派遣消息,会对窗体上的窗口过程进行调用(窗口过程里面由程序员提供的窗体数据更新代码以及其它的代码)。
Windows GUI
程序的消息循环
GUI窗体上的所有消息是通过Windows
程序的消息队列所提供。在GUI程序运行时,为了不断的从队列中获取消息,Windows程序在while
循环中主要使用GetMessage()
方法,这是个阻塞方法,当Windows
消息队列为空时,此方法就会被阻塞,此时while
循环就会停止(主要避免程序把CPU耗尽,使其它程序难以得到响应。若需要CPU进行最大限度的运行(如3D
游戏),一般使用PeekMessage()
方法,它不会被Windows
阻塞,从而保证程序的流畅和比较高的帧速)。
.NET
的消息循环
public static void Main(string[] args)
{Form frm = new Form();Application.Run(frm);
}
在上述代码中,.NET
窗体封装了while
循环,Application.Run(frm)
方法开启循环过程。
3.消息机制—进程与线程间的通信
Windows
消息发送
Windows
消息机制是Windows
平台上的线程或者进程间通信机制之一。Windows
消息值的定义为一个数据结构,其中最重要的是消息类型,为一个整数;然后是消息参数,消息参数可以表达很多。
当然,Windows
也提供了一些API用来向一个线程队列发送消息。因此,一个线程可以向另一个线程的消息队列发送消息,这样就实现了线程间的通信。有些API
发送消息需要一个窗口句柄,这种函数可以把消息发送到指定窗口的主线程消息队列,而有些可直接通过线程句柄,把消息发送到该线程的消息队列中。
消息机制通信
Windows API
中提供了SendMessage()
方法,把一个消息发送到一个窗口的消息队列。这个方法为阻塞方法,操作系统会确保消息确实发送到目的消息队列,并且该消息被处理后,该方法才返回,并将控制权返还给调用者,在返回之前,调用者将暂时被阻塞。
PostMessage()
方法也是发送消息到窗口的消息队列,由Windows API
所提供,但这个方法是非阻塞,调用者在调用该方法后,会立即返回,操作系统不会确保消息是否的确发送到目的消息队列中,调用者也不会被阻塞。
Invoke
和BeginInvoke
方法
由上述可知,Invoke
和BeginInvoke
方法由ISynchronizeInvoke
接口提供,且都需要一个委托对象作为参数。委托类似于回调函数的地址,调用者可通过这两个方法将需要调用的函数地址封送到界面线程。如果这些封送给界面线程的方法包含了更改界面控件的代码,由于最终执行这个方法是在界面控件线程中,从而避免了线程之间的竞争。
使用Invoke
方法完成委托的封送,类似于使用SendMessage
方法给界面线程发送消息,为同步方法。在Invoke
封装的方法未执行完毕之前,Invoke
方法是不会返回,使调用者线程被阻塞。
使用BeginInvoke
方法完成委托的封送,类似于使用PostMessage
方法给界面线程发送消息,为异步方法。在完成委托的封送后,BeginInvoke
方法立即返回,不会等待委托的方法执行完毕,调用者线程将不会阻塞。但是调用者可以使用EndInvoke
方法或者其它类似WaitHandle
机制等待异步操作的完成。
但是,Invoke
和BeginInvoke
在内部的实现上都是使用PostMessage
方法,从而避免SendMessage
方法带来的问题。其中Invoke
方法的同步阻塞是使用WaitHandle
机制完成。
使用场合
若你的后台线程在更新UI
界面且不需要等待,则应该使用BeginInvoke
方法进行异步操作。
若你的后台线程在更新UI
界面并且需要等待,则应该使用Invoke
方法进行同步操作。
在ISynchronizeInvoke
接口中,可以看到还有一个InvokeRequired
。这个属性的作用是:在编程时确定一个对象在访问UI
控件时是否需要使用Invoke
和BeginInvoke
方法进行封送,如果不需要则可以直接进行更新。在调用者对象和UI
对象属于同一个线程时,该属性返回false
。Control类对这一属性的实现是在判断调用者和控件是否同属于一个线程。
Delegate.BeginInvoke
通过一个委托来进行同步方法的异步调用,也是.NET
提供的异步调用机制之一。但是Delegate.BeginInvoke
方法是从ThreadPool
中取出的一个线程来执行这个方法,以获得异步的执行效果。如果采用这种方式提交多个异步委托,这些调用的顺序无法得到保障。而且由于使用的是线程池中的线程来完成任务,若频繁的使用,会对系统的性能造成影响。
在Control
类中的BeginInvoke
方法没有开辟新的线程完成委托任务,而是让界面所属的控件线程完成委托任务(异步操作就是开辟新的线程的说法也不一定准确)。
Control.BeginInvoke
和Control.Invoke
public IAsyncResult BeginInvoke(Delegate method, params object[] args)
{using (new MultithreadSafeCallScope()){return (IAsyncResult) this.FindMarshalingControl().MarshaledInvoke(this, method, args, false);}
}public object Invoke(Delegate method, params object[] args)
{using (new MultithreadSafeCallScope()){return this.FindMarshalingControl().MarshaledInvoke(this, method, args, true);}
}
从上述代码可以看到Invoke
和BeginInvoke
方法使用了同样的实现,只是MarshaledInvoke
方法的最后一个参数值不一样,一个为true
,另一个为false
。
这里的FindMarshalingControl
方法通过一个循环向上回溯,从当前控件开始回溯父控件,直到找到最顶级的父控件,用它作为封送对象。例如,我们调用窗体上一个TextBox
控件的Invoke
方法封送委托,但是实际上会回溯到主窗体,通过这个控件对象来封送委托。因为主窗体是主线程消息队列相关的,发送给主窗体的消息才能发送到界面主线程消息队列。
Invoke和BeginInvoke的详细理解(C#)相关推荐
- 我理解的invoke和begininvoke
我理解的invoke和begininvoke 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解. 首先说下,invoke和be ...
- .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调、APM、EAP、TPL、aysnc、await
windows系统是一个多线程的操作系统.一个程序至少有一个进程,一个进程至少有一个线程.进程是线程的容器,一个C#客户端程序开始于一个单独的线程,CLR(公共语言运行库)为该进程创建了一个线程,该线 ...
- 委托的Invoke 和 BeginInvoke 与Control的Invoke和BeginInvoke(转-因为写得很好)
原文地址:http://www.cnblogs.com/worldreason/archive/2008/06/09/1216127.html Invoke and BeginInvoke Invok ...
- c# Invoke和BeginInvoke 区别详解
Control.Invoke 方法 (Delegate):在拥有此控件的基础窗口句柄的线程上执行指定的委托. Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄 ...
- C#中Invoke 和 BeginInvoke的涵义和区别
BeginInvoke 方法真的是新开一个线程进行异步调用吗? 参考以下代码: public delegate void treeinvoke(); private void UpdateTreeVi ...
- 【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)
今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别.所以花了点时间研究了下. 据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的. Cont ...
- 浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别
今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别.所以花了点时间研究了下. 据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的. Cont ...
- C#:invoke 与 BeginInvoke使用区别
invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解. 首先说下,invoke和beg ...
- C#中Invoke 和 BeginInvoke 的区别
Invoke 和 BeginInvoke 的真正涵义 .在多线程中如何调用Winform Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托. ...
最新文章
- 条理清晰的搭建SSH环境之添加所需jar包
- 【转】使用python3的typing模块提高代码健壮性
- WinAPI——Windows 消息
- C#正则表达式判断输入日期格式是否正确
- ROS-WIKI——编写简单的发布者和订阅者(C++和Python版本)
- java面向对象特性_java面向对象编程三大特性
- Linux的链接工具 putty 以及一些命令。
- c#连接kafka_c#操作kafka(上)搭建kafka环境
- Windows下安装最新的Apache+PHP+MySQL方法--记录方便自己参考
- springboot - 应用实践(N)使用springboot内置的@Scheduled
- 实用的BeanUtils工具类
- linux 清除终端记录,清除Linux终端命令的历史记录
- 呆萝卜 竞品分析报告
- 计算机信息与科学专业好吗,俄亥俄州立大学 计算机信息与科学这个专业怎么样...
- medusa详细教程
- Mysql - 开发技巧(二)
- Java毕设_装修公司业务流程管理系统的设计与实现
- 渲染多层材料的综合框架
- Python语言程序设计基础 第二版(嵩天著)课后答案第四章
- STM32——时钟系统RCC详细介绍
热门文章
- JSON.parse
- Docker command line 学习笔记
- 【数电试题】西电通卓模拟卷三
- linux系统第一篇(Linux系统入门介绍)
- try和catch的用法
- 网友质问微软客服:为啥不黑我?!(爆笑)
- 【LeetCode】1609. 奇偶树、1122. 数组的相对排序
- 如何使用Tableau分析敏捷,开发和网站指标
- python爬虫基础小案例, scrapy框架,思路和经验你全都有。
- 复旦非全日制研究生计算机,2018年复旦大学信息科学与工程学院非全日制招生计划...