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相关推荐

  1. Mcad学习笔记之委托再理解(delegate的构造器,MulticastDelegate,BeginInvoke,EndInvoke,Invoke4个方法的探讨)...

    相关文章导航 Sql Server2005 Transact-SQL 新兵器学习总结之-总结 Flex,Fms3相关文章索引 FlexAir开源版-全球免费多人视频聊天室,免费网络远程多人视频会议系统 ...

  2. C# 学习笔记(15)自己的串口助手----波形显示

    C# 学习笔记(15)自己的串口助手----波形显示 chart控件 chart控件共有5大集合,最重要的两个集合就是绘图空间和线 坐标系 坐标系的设置在绘图空间集合内 设置坐标系样式 框选放大功能 ...

  3. C# 学习笔记(9)线程

    C# 学习笔记(9)线程 本文参考博客 C#多线程 https://www.cnblogs.com/dotnet261010/p/6159984.html C# 线程与进程 https://www.c ...

  4. C# 学习笔记(8) 控件的跨线程访问

    C# 学习笔记(8) 控件的跨线程访问 本文参考博客 C#多线程 https://www.cnblogs.com/dotnet261010/p/6159984.html C# 线程与进程 https: ...

  5. WPF基础学习笔记(一)Dependency Object 和 Dependency Property

    .依赖属性是WPF个人觉得对精彩和最有特色的部分.所以特地先拿出来. 首先要实现Dependency Property 则必须要继承Dependency Object.如果看下WPF的基础控件其实都间 ...

  6. Mcad学习笔记之通过反射调用類的方法,屬性,字段,索引器(2種方法)

    相关文章导航 Sql Server2005 Transact-SQL 新兵器学习总结之-总结 Flex,Fms3相关文章索引 FlexAir开源版-全球免费多人视频聊天室,免费网络远程多人视频会议系统 ...

  7. 委托(C#入门详解学习笔记)

    委托(C#入门详解学习笔记) 几个概念 什么是委托 委托的声明(自定义委托) 委托的常规使用 通用泛型委托类型的简单使用(Func和Action) 委托的高级使用 多播委托 委托的异步调用 使用接口取 ...

  8. C# async await 学习笔记2

    C# async await 学习笔记1(http://www.cnblogs.com/siso/p/3691059.html) 提到了ThreadId是一样的,突然想到在WinForm中,非UI线程 ...

  9. java学习笔记13--反射机制与动态代理

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...

最新文章

  1. 遍历Repeater与ItemDataBound事件发现的几个问题。
  2. 用java程序编写ip仿真器_用java 编写一个可以实现IP地址查询功能的课程设计
  3. sql语句字符串处理大全
  4. 斯坦福java下载_斯坦福解析器java错误
  5. uni-app—微信公众号授权登录(截取code)
  6. Mysql 零距离-入门(五)操作数据表
  7. div+css使多行文字垂直居中?
  8. HTML5:去除IE10中输入框和密码框的X按钮和小眼睛
  9. elementUI中input的使用
  10. 77----空间直角坐标变换、平移、旋转、伸缩
  11. Win11如何更新BIOS?
  12. Windows 必备纯净软件
  13. lol服务器是用什么系统,能玩lol的云服务器
  14. VMware安装CentOS 7.0 Fail to start media check on /dev/sr0
  15. javascript使用小技巧
  16. Springboot 阿里云OSS修改下载文件名称
  17. 如何更新R以及RStudio
  18. 每任务-苹果应用市场隐私政策
  19. 互联网进入存量博弈时代,小程序技术创造移动应用新机遇
  20. H.264/AVC 中的宏块、片、帧

热门文章

  1. 用于HTTP加密浏览的TW2.0插件
  2. linux安装tree命令
  3. 优化一个小时不出结果的SQL
  4. linux web服务器,防火墙iptables最简配置
  5. python每日一类(3):os和sys
  6. 记于2014-12-9
  7. C++ dll 动态链接库的创建与调用
  8. 如何在GridView中使用DataFromatString [转]
  9. 算法---给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合
  10. excel相乘后求和_Excel求和只会sum函数就out了,这五个求和公式一个比一个强