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. 编程实现 4 条重写规则,可生成半法式和法式
  2. MYsql可以做独立数据库_mysql-用于独立应用程序Winforms的数据库
  3. java ftp 判断目录存在_java判断ftp目录是否存在的方法
  4. P4587-[FJOI2016]神秘数【主席树】
  5. 计算机图形学学习报告,计算机图形学学习报告.doc
  6. Android知识体系框架
  7. 树莓派7寸触屏,略贵
  8. 十大 Node.js 的 Web 框架,快速提升工作效率
  9. [转载] 深入理解log机制
  10. mysql年龄数据类型_MySql 数据类型
  11. md5加密算法c语言版
  12. 正态分布c语言算法,正态分布函数的几种近似算法
  13. html之使用session进行服务器之间的跳转以及记录
  14. 生活照无处不在的细节决定着你的命运
  15. perl Data::Dumper和Storable的例子
  16. android横竖屏切换布局闪退,Android 横竖屏切换以及横屏启动闪退问题
  17. 艺展中心七夕游园雅集,梦回长安品古韵
  18. 尴尬了!一个妹子 rm -rf 把公司整个数据库删没了...
  19. 强人工智能和弱人工智能的区别,你知道吗?
  20. java+vue3实现生成、验证图形验证码,和手机短信验证码

热门文章

  1. 对于css的简化属性
  2. 使用VS Code新建编译Flutter项目
  3. jstack可以定位到线程堆栈
  4. 解决Caused by: java.lang.NoSuchMethodException: com.mchange.v2.c3p0.cfg.C3P0Config.init()的总结...
  5. centos 6.5下编译安装、配置高性能服务器Nginx
  6. layout-maxWidth属性用法
  7. 如何学好3D游戏引擎编程《转自3D游戏引擎网》
  8. RecycleView加载不同类型的Item
  9. Android中Parcelable接口用法
  10. android的dmtracedump工具生成trace文件图片 'dot' 不是内部或外部命令,也不是可运行的程序 或批处理文件。