.Net组件程序设计之异步调用
.Net组件程序设计之异步调用
说到异步调用,在脑海中首先想到就是BeginInvoke(),在一些常用对象中我们也会常常见到Invoke()和BeginInvoke(), 要想让自己的组件可以被客户端调用或者是异步调用,这样的设计是合理的,这也是组件异步机制当中的一条 (说句题外话--其实大多数知识都隐藏在我们平时经常见到的对象或者是代码里,只不过是没有去细心的发现) 在.NET中首先就会想到使用委托来进行异步调用,关于委托的定义在 委托与事件一文中已经大概的说过了,文中只是对委托进行了 大概的讲解,并没有对委托的使用来说明或者是例举一些示例。 在本篇中将会对委托进行一个基础的揭底,主要方向是异步调用。
一 委托的老调重弹
- public class Operation
- {
- public int Addition(int num1, int num2)
- {
- return num1 + num2;
- }
- public int Subtraction(int num1, int num2)
- {
- return num1 - num2;
- }
- }
没有必要直接使用Operation对象来进行加减运算,可以使用委托:
- public delegate int OperationDelegate(int num1, int num2);
- Operation operation = new Operation();
- OperationDelegate Additiondelegate = operation.Addition;
- int result;
- result = Additiondelegate.Invoke(, );
- Debug.Assert(result == );
在使用委托进行调用的时候,当前线程是被阻塞的,只有当委托执行完毕了,才会把控制权交回到当前线程。
不过呢,委托可以用于进行异步调用目标方法的,委托只是一种特定的类型,编译器会把我们定义的各式各样的委托编译成
对应的类,好比OperationDelegate委托一样,实则是被编译成这样的
- public sealed class OperationDelegate : MulticastDelegate
- {
- public OperationDelegate(Object target, int methodPtr) { }
- public virtual Invoke(int num1,int num2)
- {
- ……
- }
- public virtual IAsyncResult BeginInvoke(int num1,int num2,AsyncCallback
- callback,object asyncState)
- {
- ……
- }
- public virtual int EndInvoke(IAsyncResult result)
- {
- ……
- }
- }
这里只是回顾一下委托的定义。
二 异步调用编程模型
图1
在上图我们所见的有这几个模块, .NET线程池、异步调用请求队列和一个应用程序的主线程。
假使现在从任务1开始执行到任务2、任务3,到了任务3的时候,任务3请求.NET执行异步操作,如图2
图2
这个时候【任务3】已经被送入到了【异步请求队列】中,并且主线程是阻塞状态的,再看图3的执行过程:
图3
线程池会及时的发现【异步请求队列】中的任务,并且根据任务的信息,线程池会分配一个线程到任务所在的主线程中执行所请求的任务。 在异步任务执行时,这个时候主线程才会从阻塞中撤销,进入执行状态,上图中,就是开始执行任务4。
这里要说的就是,异步调用看起来是并行执行的,实际刚开始的时候还是顺序的,不过这时间在实际情况中是忽略不计的, 可以认为就是并行执行的吧。
三 BeginInvoke()、EndInvoke()
3.1 BeginInvoke()
BeginInvoke()函数定义如下:
- public virtual IAsyncResult BeginInvoke(int num1,int num2,AsyncCallback callback,object asyncState)
- {
- ……
- }
接受OperationDelegate委托定义的原始签名的输入参数,还有两个额外参数,AsyncCallback是系统定义的委托, 用于异步调用完成时回调所用,这里不做讲解,后面会有讲到,还有一个是参数是一个状态对象,也可以认为是容器对象, 也会在后面的章节中讲到。
- Operation operation = new Operation();
- OperationDelegate Additiondelegate = operation.Addition;
- Additiondelegate.BeginInvoke(, , null, null);
3.2 IAsyncResult接口
正如上面所看到的,BeginInvoke函数返回一个IAsyncResult类型的值,那就来看一下IAsyncResult的定义:
- public interface IAsyncResult
- {
- object AsyncState { get; }
- WaitHandle AsyncWaitHandle { get; }
- bool CompletedSynchronously { get; }
- bool IsCompleted { get; }
- }
对于IAsyncResult的详细用法 稍后会有讲解
看到第一节的Invoke函数执行后,可以直接获取到返回值,怎么这个BeginInvoke函数执行了返回
IAsyncResult类型,返回值在哪呢? 可以通过从BeginInvoke函数获得的IAsyncResult交给EndInvoke函数来获取返回值。
- Operation operation = new Operation();
- OperationDelegate Additiondelegate = operation.Addition;
- IAsyncResult asyncResult = Additiondelegate.BeginInvoke(, , null, null);
- int result = Additiondelegate.EndInvoke(asyncResult);
- Debug.Assert(result == );
这里要说几点
第一.调用EndInvoke函数的时候,当前线程是被阻塞的,它在等待BeginInvoke函数执行完毕。
第二.虽然委托可以管理多个目标方法,但是在异步调用中,所执行异步调用的委托,内部的管理列表只能有一个目标方法,不然会报 有异常。
第三.EndInvoke()在每次异步调用操作时 只能调用一次。
第四.BeginInvoke()返回的IAsyncResult类型的实例,只能传入它所调用BeginInvoke()委托的EndInvoke()中,不然也会报有异常。
3.3 AsyncResult
假使一个客户端在一个代码段或者是函数中使用BeginInvoke(),而在另一段或者是其他的函数中调用EndInvoke(),这样客户端是不是就要保存IAsyncResult对象,又或者一个客户端发起异步调用,并且由另一个 客户端来调用EndInvoke(),这不仅仅要保存IAsyncResult对象,还需要保存该委托对象,而且你还得传送过去。 还好.NET是那么的机智,有System.Runtime.Remoting.Messaging.AsyncResult类型的存在。
- public class AsyncResult : IAsyncResult, IMessageSink
- {
- #region IAsyncResult 成员
- public object AsyncState
- {
- get { throw new NotImplementedException(); }
- }
- public System.Threading.WaitHandle AsyncWaitHandle
- {
- get { throw new NotImplementedException(); }
- }
- public bool CompletedSynchronously
- {
- get { throw new NotImplementedException(); }
- }
- public bool IsCompleted
- {
- get { throw new NotImplementedException(); }
- }
- #endregion
- public bool EndInvokeCalled { get; set; }
- public virtual object AsyncDelegate { get; }
- //IMessageSink 成员
- }
看着上面有个AsyncDelegate的属性,会不会觉得很漂亮,不错,它就是原始发起委托的引用,看下如何使用AsyncDelegate来使用EndInvoke():
- public class OperationTest
- {
- public void Test()
- {
- Operation operation = new Operation();
- OperationDelegate Additiondelegate = operation.Addition;
- int Result;
- Result = GetResult(Additiondelegate.BeginInvoke(, , null, null));
- }
- private int GetResult(IAsyncResult asyncresult)
- {
- AsyncResult asyncResult = (AsyncResult)asyncresult;
- OperationDelegate operationdelegate = asyncResult.AsyncDelegate as
- OperationDelegate;
- if (operationdelegate != null)
- {
- Debug.Assert(asyncResult.EndInvokeCalled == false);//EndInvoke()是否被调用过
- return operationdelegate.EndInvoke(asyncResult);
- }
- return -;
- }
- }
3.4 轮循或等待
看到这里,善于思考的朋友会发现,还存在着一个很大的问题,就是发起异步调用的客户端,如何知道自己 的异步函数是否执行完毕了?或者是想等待一会,做一些其他的处理,然后再继续等待,该怎么来实现呢?
从BeginInvoke()返回的IAsyncResult接口有个AsyncWaitHandle属性,它是干吗的呢?就把它理解为消息接收器吧。
- Operation operation = new Operation();
- OperationDelegate Additiondelegate = operation.Addition;
- IAsyncResult asyncResult = Additiondelegate.BeginInvoke(, , null, null);
- asyncResult.AsyncWaitHandle.WaitOne();//如果任务完成则不会阻塞 否则阻塞当前线程
- int Result;
- Result = Additiondelegate.EndInvoke(asyncResult);
- Debug.Assert(Result == );
代码和3.2的几乎相同,区别就是这段代码保证了EndInvoke()的调用者不会被阻塞。
看一下等待一下,如果没完成处理其他任务,回来再等待是怎么实现的。
- Operation operation = new Operation();
- OperationDelegate Additiondelegate = operation.Addition;
- IAsyncResult asyncResult = Additiondelegate.BeginInvoke(, , null, null);
- while (asyncResult.IsCompleted == false)//判断异步任务是否完成
- {
- asyncResult.AsyncWaitHandle.WaitOne(,false);//如果任务完成则不会阻塞 否则阻塞当前线程10毫秒
- //这里做一些其他操作
- }
- int Result;
- Result = Additiondelegate.EndInvoke(asyncResult);
- Debug.Assert(Result == );
3.5 使用回调函数
现在我们要来说说BeginInvoke()的第三个参数了, public delegate void AsyncCallback(IAsyncResult ar);
第三个参数就是系统提供的一个委托类型,委托签名也都看到了。 使用回调函数的好处就是不需要去处理等待操作了,因为在异步任务完成的时候, 会调用你传给BeginInvoke()里AsyncCallback委托所关联的目标方法。
- public class OperationTest
- {
- public void Test()
- {
- Operation operation = new Operation();
- OperationDelegate Additiondelegate = operation.Addition;
- Additiondelegate.BeginInvoke(, , new AsyncCallback(OnCallBack), null);
- }
- private void OnCallBack(IAsyncResult asyncresult)
- {
- AsyncResult asyncResult = (AsyncResult)asyncresult;
- OperationDelegate operationdelegate = asyncResult.AsyncDelegate as
- OperationDelegate;
- if (operationdelegate != null)
- {
- Debug.Assert(asyncResult.EndInvokeCalled == false);
- int result=operationdelegate.EndInvoke(asyncResult);
- Console.WriteLine("Operation returned" + result.ToString());
- }
- }
- }
这里需要说的是在异步任务完成时,执行的回调函数依然是在子线程当中,并不是在主线程中执行回调函数的。
题外话:最常见的就是在Winform开发中,Form中发起异步调用,然后回调函数操作Form中的控件或者是
值的时候就会报错, 就是这个原因,因为它们不在一个线程也不在一个上下文中,基于.NET安全策略这种操作是不允许的。
END
作者:金源
出处:http://www.cnblogs.com/jin-yuan/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面
.Net组件程序设计之异步调用的更多相关文章
- .Net组件程序设计之远程调用(二)
.Net组件程序设计之远程调用(二) 激活模式 引用封送对象激活类型两种, 一种是客户端激活类型,一种是服务器端激活. 客户端激活对象 客户端激活方式:当客户端创建一个远程对象时,客户端得到的是一个新 ...
- .Net组件程序设计之远程调用(一)
.Net组件程序设计之远程调用(一) 1应用程序域 我们知道我们写的C#代码是在操作系统逻辑体系结构中最上层的,然而操作系统本身是不会认识C#代码的,它只认识机器代码.那我们写的程序经过编译后是编译成 ...
- .NET组件程序设计之线程、并发管理(二)
.Net组件程序设计之线程.并发管理(二) 2.同步线程 手动同步 监视器 互斥 可等待事件 同步线程 所有的.NET组件都支持在多线程的环境中运行,可以被多个线程并发访问,如果没有线程同步,这样的后 ...
- .Net组件程序设计之线程、并发管理(一)
.Net组件程序设计之线程.并发管理(一) 1.线程 线程 线程的创建 线程的阻塞 线程挂起 线程睡眠 加入线程 线程中止 现在几乎所有的应用程序都是多线程的,给用户看来就是一个应用程序界面(应用程序 ...
- .Net组件程序设计之序列化
.Net组件程序设计之序列化 自动序列化 本篇给大家讲解一下在.NET中的序列化,它的用处非常之多,特别是对于某种环境下保存某种状态是很好的方法,接下来就来看一下吧. Serializable属性 ...
- .Net组件程序设计之上下文
.Net组件程序设计之上下文 在后续篇幅的远程调用的文章里有说到应用程序域,那是大粒度的控制程序集的逻辑存在,那么想对对象的控制又由谁来做主呢?没错了,就是上下文.CLR把应用程序域更细化了,在应用程 ...
- .Net组件程序设计之对象生命周期
.Net组件程序设计之对象生命周期 .NET 垃圾回收 IDisposable() Using语句 .NET 垃圾回收 是CLR管理着垃圾回收器,垃圾回收器监控着托管堆,而我们使用的对象以及系统启动是 ...
- .Net组件程序设计
.Net组件程序设计之上下文 在后续篇幅的远程调用的文章里有说到应用程序域,那是大粒度的控制程序集的逻辑存在,那么想对对象的控制又由谁来做主呢?没错了,就是上下文.CLR把应用程序域更细化了,在应用程 ...
- SpringBoot中异步请求和异步调用(看这一篇就够了)
原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10661591.html,否则将追究法律责任!!! 一.SpringBoot中异步请求的使用 ...
随机推荐
- Startcom SSL证书申请 IIS设置 配置 攻略
申请具体参考:http://www.cnblogs.com/yibinboy/p/6137426.html 制作要导入服务器IIS上的证书. 点击控制面板的左上角的TOOL BOX,然后点击Creat ...
- OS X 下不通过Homebrew安装ASP.NET 5开发环境
在 ASP.NET 的 Home repo 里,推荐使用 Homebrew 安装开发环境,不过我的电脑里已经有 ports 了,这应该是当年用 rvm 安装 Ruby 时悄悄地装上的吧.不管怎样,作为 ...
- python+Eclipse+pydev环境搭建
python+Eclipse+pydev环境搭建 本文重点介绍使用Eclipse+pydev插件来写Python代码, 以及在Mac上配置Eclipse+Pydev 和Windows配置Ecli ...
- 代码在ie9中不能正确执行
<!DOCTYPE html> <html> <head lang="zh"> <meta charset="UTF-8&quo ...
- CustomUI Direct3D9_Sample
刚开始建这个项目的时候编译器报了很多Link2019的错误. 后来添加了一些lib文件才解决,参考 缺少.lib文件导致的Link2019 解决方案汇总 ==================== ...
- RStudio技巧02_Extract Function
RStudio 可以在 source 编辑器中分析一组选择的代码,并自动将其转化成再次使用的函数.任何选择中的"free"变量( 选择引用对象但不创建)将转化为函数参数. (也可使 ...
- haha2
# YOU - fhasd - fdks jf > jd sfkjd sf ```python print "helloworld" ``` 来自为知笔记(Wiz)
- JavaScript解惑记之Array.prototype.sort()
前言 看JS红宝书的5.2.5章节关于sort()方法,如何用一个compare函数,让数组顺序,倒序,有点云里雾里的.在网上度娘了一下,发现更迷糊了.走投无路的情况下,只能发动神技能,去 stack ...
- Redis安装配置(Windows版)
近期项目中引入Redis,故记录下来,方便日后查看. 可参考(http://www.cnblogs.com/happyday56/p/3916388.html)不说废话,直奔主题. 一.安装前的准备: ...
- linux进程后台运行及输出重定向
本机环境为ubuntu 14.04 以ping www.baidu.com为例: 1.程序的前台运行 ping www.baidu.com 可以看到,屏幕上输出了baidu返回的结果 2.实现程序后台 ...