大马哈鱼的C#学习笔记(3):Invoke/BeginInvoke/DynamicInvoke
Control.Invoke和Control.BeginInvoke
Control.Invoke()是同步方法,它会往Control所处的线程(UI主线程)消息队列中加一条消息,请求执行某个委托,在该委托方法(可能多个)执行完毕后,Control.Invoke()得以返回,继续执行下一行代码。当委托的InvocationList元素有多个时,即有多个方法需要执行时,Control.Invoke()返回值是最后一个执行的方法的返回值。
Control.BeginInvoke是异步方法,它往Control所处的线程(UI主线程)消息队列中加一条消息,请求执行某个委托,然后立刻返回。至于请求的方法何时开始执行,它不关心。但是请求的方法执行的结果,它必然要关心,所以Control.BeginInvoke()有一个IAsyncResult的返回值,这个IAsyncResult可以看作委托的凭据。Control.BeginInvoke()调用后,可以在任意时间后调用Control.EndInvoke(IAsyncResult)来获得该凭据对应的执行结果。如果尚未执行完,则一直在那等,直到执行完成为止。EndInvoke的返回值是一个object,即为委托方法的返回值(同样地,如果有多个委托方法,则为最后一个方法的返回值)。
值得注意的是,EndInvoke只能被调用一次,否则会引发异常。
在调用Control.Invoke/BeginInvoke之前,可以先使用Control.InvokeRequired检测是否调用者当前线程跟Control创建线程不同,如果不同,则表示必须使用Invoke来调用对Control进行操作的方法,否则可以直接调用。
以下代码可以用来演示Control.InvokeRequired的使用:
private delegate int AddDel(int value);
private AddDel addDel = (value) =>
{
return ++value;
};
private void button1_Click(object sender, EventArgs e)
{
Button button = sender as Button;
if(button.InvokeRequired) //一定为false
{
//blabla
}
//请求使用线程池中的线程来执行workThreadMathod
ThreadPool.QueueUserWorkItem(workThreadMathod, sender);
}
private void workThreadMathod(object obj)
{
Button button = obj as Button;
if (button.InvokeRequired) //一定为 true
{
//blabla
}
}
Control.BeginInvoke返回的IAsyncResult不能强转为AsyncResult,AsyncResult类只用于Delegate的BeginInvoke。
IAsyncResult. AsyncWaitHandle.WaitOne()如果被调用,则一直会等到委托方法执行完毕后返回,这应该就是EndInvoke的内部实现。但是它也开放出来给用户使用了,因为有些场合,既需要等候委托方法的完成,又需要在取得委托方法的返回值之前(调用EndInvoke)干点别的什么事,那么就需要手动调用WaitOne()了。当然,如果没有这种变态需求,直接调用EndInvoke即可。IAsyncResult. AsyncWaitHandle.WaitOne()使用后,需要调用Close()来关闭句柄。
有一点需要注意的是,如果Control.BeginInvoke是在创建Control的线程(主线程)中被调用,那么一定不能使用IAsyncResult. AsyncWaitHandle.WaitOne(),因为这样会造成死锁,调用处在无限等待,委托方法又得不到执行,线程就一直被挂起了。
Delegate.Invoke 和 Delegate.BeginInvoke:
跟Control.Invoke、Control.BeginInvoke类似的,前者是同步方法,后者是异步方法。
Delegate.Invoke使用起来就跟直接调用方法类似,参数就是委托的参数类型。
也可以使用完全跟方法调用一样的形式,即加括号()发方式来调用,内部实现上依旧是调用的Invoke方法。
Delegate.Invoke除了是同步的以外,还有一个显著特征是,委托方法执行在调用处同一个线程中。
Delegate.BeginInvoke则不同,它除了是异步的以外,还有个很重要的特征,它从线程池中抓取一个空闲线程,来执行委托方法。
看一下Delegate.BeginInvoke的签名:
public class xxxDelegate : MulticastDelegate
{
public IAsyncResult BeginInvoke(...,AsyncCallback cb, object @object);
}
...处是委托方法的参数列表,cb是一个AsyncCallback实例,AsyncCallback的定义如下:
public delegate void AsyncCallback(IAsyncResult ar);
@object是调用者自己传入的对象,到时候在AsyncCallback委托的方法中可以获取到。
下面结合实例讲一下BeginInvoke的使用流程:
private delegate int AddDel(int value);
private AddDel addDel = (value) =>
{
return ++value;
};
private void button1_Click(object sender, EventArgs e)
{
IAsyncResult iar = addDel.BeginInvoke(33, (v) => //1
{
AsyncResult ar = v as AsyncResult; //2
AddDel del = ar.AsyncDelegate as AddDel; //3
string inputObj = ar.AsyncState as string; //4
if (!ar.EndInvokeCalled) //5
{
int result = (int)del.EndInvoke(ar); //6
}
},@"abc"); //7
int result1 = addDel.EndInvoke(iar); //8
}
为了方便序数,以上事例各行都编了号。
第1行,请求线程池安排一个空闲线程来执行addDel的委托方法,参数为33
第2~6行,使用一个lambda表达式作为BeginInvoke的AsyncCallback参数,表示说在addDel的委托方法返回后,需要执行该AsyncCallback实例的委托方法,即执行花括号内的代码块。注意,这个代码块运行于addDel委托方法同一个线程中。
第2行,将IAsyncResult类型的v强转成AsyncResult对象,这个跟Control.BeginInvoke的返回值不同,只有Delegate.BeginInvoke的返回值才是一个AsyncResult实例。
同时,ar和第1行的iar实际上是同一个引用。
第3行,从ar中拿到委托实例,del和addDel实际上是同一个引用。
第4行,从ar中拿到传入的对象,即第7行的@”abc”。
第5行,判断EndInvoke是否已经调用过了。因为EndInvoke只能调用一次,所以提供这个方法来提前判断。
第6行,调用EndInvoke来获取addDel委托方法的返回值。
第8行,这行代码所处的线程和2~6行所处的线程是不同的,因此实际上是无法知道到底第8行跟第6行哪一个EndInvoke会真正执行,而且,极端的情况下,如果第6行得到执行的话,那第8行是会引发异常的,同样,如果第8行执行完后,第6行所处的线程得到调度,那第6行也会引发异常。第5行的EndInvokeCalled理论上在这里毫无作用。
以上只是演示了Delegate.BeginInvoke的使用。实际的编码过程中,应该要避免在Delegate.BeginInvoke的调用线程以及AsyncCallback委托方法中同时进行EndInvoke的处理逻辑。
至于到底把后续逻辑放在哪一块来执行,就得看需求而定了,如果是耗时的后台操作,那么放在AsyncCallback的委托方法中比较好;如果addDel.BeginInvoke调用处本身也是一个工作线程,那似乎放到第8行以后来处理会比较好,毕竟可以不用切换上下文环境。
Delegate.DynamicInvoke
跟Delegate.Invoke类似,同步,且同线程,唯一不同的是,是采用后期绑定的方式来调用委托方法。
假如一个Delegate(注意D是大小的,表示类实例),必须要到运行时动态来确定其方法签名(即该Delegate实例是通过Delegate.CreateDelegate()动态创建的),那就要用到DynamicInvoke()。查看Delegate类的结构,发现DynamicInvoke是一个public方法,同时还有protected virtual object DynamicInvokeImpl(object[] args);这么一个虚方法。猜测通过delegate关键字定义的类,应该是重写了这个虚方法。
那么如果对一个通过Delegate.CreateDelegate()动态创建的Delegate实例调用Invoke会怎样?答案是没有这回事,因为Delegate根本就没有Invoke方法。
对于通过delegate关键字定义的Delegate类,由于编译期间已经确定了其方法签名,那么用Invoke或者DynamicInvoke,效果是一样的。当然,由于DynamicInvoke在编译时不检查方法签名是否匹配,如果在运行时发现不匹配的话,会引发异常。Invoke由于在编译期间就已经确保了签名匹配,所以运行期间不会引发参数不匹配异常。
WP7中的BeginInvoke
目前版本的wp7中,Control.Invoke以及Control.BeginInvoke都不见了踪影。
Delegate.Invoke可用,而Delegate.BeginInvoke在运行时却会抛异常。所以很令人沮丧。
目前我找到的唯一可用的BeginInvoke,位于System.Windows.Threading.Dispatcher.BeginInvoke。
在Silverlight 4.0 for WP中,任何一个DependencyObject都有一个Dispatcher属性,可以获取到代表当前主线程的Dispatcher对象,调用其BeginInvoke即可呼叫主线程来执行提供的委托。
不过其返回值DispatcherOperation对象是没有任何实现的,简直坑爹呀。
转载于:https://www.cnblogs.com/laoyur/archive/2011/04/14/2016025.html
大马哈鱼的C#学习笔记(3):Invoke/BeginInvoke/DynamicInvoke相关推荐
- Mcad学习笔记之委托再理解(delegate的构造器,MulticastDelegate,BeginInvoke,EndInvoke,Invoke4个方法的探讨)...
相关文章导航 Sql Server2005 Transact-SQL 新兵器学习总结之-总结 Flex,Fms3相关文章索引 FlexAir开源版-全球免费多人视频聊天室,免费网络远程多人视频会议系统 ...
- C# 学习笔记(15)自己的串口助手----波形显示
C# 学习笔记(15)自己的串口助手----波形显示 chart控件 chart控件共有5大集合,最重要的两个集合就是绘图空间和线 坐标系 坐标系的设置在绘图空间集合内 设置坐标系样式 框选放大功能 ...
- C# 学习笔记(9)线程
C# 学习笔记(9)线程 本文参考博客 C#多线程 https://www.cnblogs.com/dotnet261010/p/6159984.html C# 线程与进程 https://www.c ...
- C# 学习笔记(8) 控件的跨线程访问
C# 学习笔记(8) 控件的跨线程访问 本文参考博客 C#多线程 https://www.cnblogs.com/dotnet261010/p/6159984.html C# 线程与进程 https: ...
- WPF基础学习笔记(一)Dependency Object 和 Dependency Property
.依赖属性是WPF个人觉得对精彩和最有特色的部分.所以特地先拿出来. 首先要实现Dependency Property 则必须要继承Dependency Object.如果看下WPF的基础控件其实都间 ...
- Mcad学习笔记之通过反射调用類的方法,屬性,字段,索引器(2種方法)
相关文章导航 Sql Server2005 Transact-SQL 新兵器学习总结之-总结 Flex,Fms3相关文章索引 FlexAir开源版-全球免费多人视频聊天室,免费网络远程多人视频会议系统 ...
- 委托(C#入门详解学习笔记)
委托(C#入门详解学习笔记) 几个概念 什么是委托 委托的声明(自定义委托) 委托的常规使用 通用泛型委托类型的简单使用(Func和Action) 委托的高级使用 多播委托 委托的异步调用 使用接口取 ...
- C# async await 学习笔记2
C# async await 学习笔记1(http://www.cnblogs.com/siso/p/3691059.html) 提到了ThreadId是一样的,突然想到在WinForm中,非UI线程 ...
- java学习笔记13--反射机制与动态代理
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...
最新文章
- 遍历Repeater与ItemDataBound事件发现的几个问题。
- 用java程序编写ip仿真器_用java 编写一个可以实现IP地址查询功能的课程设计
- sql语句字符串处理大全
- 斯坦福java下载_斯坦福解析器java错误
- uni-app—微信公众号授权登录(截取code)
- Mysql 零距离-入门(五)操作数据表
- div+css使多行文字垂直居中?
- HTML5:去除IE10中输入框和密码框的X按钮和小眼睛
- elementUI中input的使用
- 77----空间直角坐标变换、平移、旋转、伸缩
- Win11如何更新BIOS?
- Windows 必备纯净软件
- lol服务器是用什么系统,能玩lol的云服务器
- VMware安装CentOS 7.0 Fail to start media check on /dev/sr0
- javascript使用小技巧
- Springboot 阿里云OSS修改下载文件名称
- 如何更新R以及RStudio
- 每任务-苹果应用市场隐私政策
- 互联网进入存量博弈时代,小程序技术创造移动应用新机遇
- H.264/AVC 中的宏块、片、帧