.Net组件程序设计之异步调用

说到异步调用,在脑海中首先想到就是BeginInvoke(),在一些常用对象中我们也会常常见到Invoke()和BeginInvoke(), 要想让自己的组件可以被客户端调用或者是异步调用,这样的设计是合理的,这也是组件异步机制当中的一条 (说句题外话--其实大多数知识都隐藏在我们平时经常见到的对象或者是代码里,只不过是没有去细心的发现) 在.NET中首先就会想到使用委托来进行异步调用,关于委托的定义在 委托与事件一文中已经大概的说过了,文中只是对委托进行了 大概的讲解,并没有对委托的使用来说明或者是例举一些示例。 在本篇中将会对委托进行一个基础的揭底,主要方向是异步调用。

一 委托的老调重弹

  1. public class Operation
  2. {
  3. public int Addition(int num1, int num2)
  4. {
  5. return num1 + num2;
  6. }
  7. public int Subtraction(int num1, int num2)
  8. {
  9. return num1 - num2;
  10. }
  11. }

没有必要直接使用Operation对象来进行加减运算,可以使用委托:

  1. public delegate int OperationDelegate(int num1, int num2);
  2.  
  3. Operation operation = new Operation();
  4. OperationDelegate Additiondelegate = operation.Addition;
  5.  
  6. int result;
  7. result = Additiondelegate.Invoke(, );
  8. Debug.Assert(result == );

在使用委托进行调用的时候,当前线程是被阻塞的,只有当委托执行完毕了,才会把控制权交回到当前线程。
不过呢,委托可以用于进行异步调用目标方法的,委托只是一种特定的类型,编译器会把我们定义的各式各样的委托编译成
对应的类,好比OperationDelegate委托一样,实则是被编译成这样的

  1. public sealed class OperationDelegate : MulticastDelegate
  2. {
  3. public OperationDelegate(Object target, int methodPtr) { }
  4. public virtual Invoke(int num1,int num2)
  5. {
  6. ……
  7. }
  8.  
  9. public virtual IAsyncResult BeginInvoke(int num1,int num2,AsyncCallback
  10.  
  11. callback,object asyncState)
  12. {
  13. ……
  14. }
  15.  
  16. public virtual int EndInvoke(IAsyncResult result)
  17. {
  18. ……
  19. }
  20. }

这里只是回顾一下委托的定义。

二 异步调用编程模型

图1

在上图我们所见的有这几个模块, .NET线程池、异步调用请求队列和一个应用程序的主线程。
假使现在从任务1开始执行到任务2、任务3,到了任务3的时候,任务3请求.NET执行异步操作,如图2

图2

这个时候【任务3】已经被送入到了【异步请求队列】中,并且主线程是阻塞状态的,再看图3的执行过程:

图3

线程池会及时的发现【异步请求队列】中的任务,并且根据任务的信息,线程池会分配一个线程到任务所在的主线程中执行所请求的任务。 在异步任务执行时,这个时候主线程才会从阻塞中撤销,进入执行状态,上图中,就是开始执行任务4。

这里要说的就是,异步调用看起来是并行执行的,实际刚开始的时候还是顺序的,不过这时间在实际情况中是忽略不计的, 可以认为就是并行执行的吧。

 三 BeginInvoke()、EndInvoke()

3.1 BeginInvoke()

BeginInvoke()函数定义如下:

  1. public virtual IAsyncResult BeginInvoke(int num1,int num2,AsyncCallback callback,object asyncState)
  2. {
  3. ……
  4. }

接受OperationDelegate委托定义的原始签名的输入参数,还有两个额外参数,AsyncCallback是系统定义的委托, 用于异步调用完成时回调所用,这里不做讲解,后面会有讲到,还有一个是参数是一个状态对象,也可以认为是容器对象, 也会在后面的章节中讲到。

  1. Operation operation = new Operation();
  2. OperationDelegate Additiondelegate = operation.Addition;
  3. Additiondelegate.BeginInvoke(, , null, null);

3.2 IAsyncResult接口

正如上面所看到的,BeginInvoke函数返回一个IAsyncResult类型的值,那就来看一下IAsyncResult的定义:

  1. public interface IAsyncResult
  2. {
  3. object AsyncState { get; }
  4. WaitHandle AsyncWaitHandle { get; }
  5. bool CompletedSynchronously { get; }
  6. bool IsCompleted { get; }
  7. }

对于IAsyncResult的详细用法 稍后会有讲解

看到第一节的Invoke函数执行后,可以直接获取到返回值,怎么这个BeginInvoke函数执行了返回

IAsyncResult类型,返回值在哪呢? 可以通过从BeginInvoke函数获得的IAsyncResult交给EndInvoke函数来获取返回值。

  1. Operation operation = new Operation();
  2. OperationDelegate Additiondelegate = operation.Addition;
  3.  
  4. IAsyncResult asyncResult = Additiondelegate.BeginInvoke(, , null, null);
  5. int result = Additiondelegate.EndInvoke(asyncResult);
  6. Debug.Assert(result == );

这里要说几点

第一.调用EndInvoke函数的时候,当前线程是被阻塞的,它在等待BeginInvoke函数执行完毕。

第二.虽然委托可以管理多个目标方法,但是在异步调用中,所执行异步调用的委托,内部的管理列表只能有一个目标方法,不然会报 有异常。

第三.EndInvoke()在每次异步调用操作时 只能调用一次。

第四.BeginInvoke()返回的IAsyncResult类型的实例,只能传入它所调用BeginInvoke()委托的EndInvoke()中,不然也会报有异常。

3.3 AsyncResult

假使一个客户端在一个代码段或者是函数中使用BeginInvoke(),而在另一段或者是其他的函数中调用EndInvoke(),这样客户端是不是就要保存IAsyncResult对象,又或者一个客户端发起异步调用,并且由另一个 客户端来调用EndInvoke(),这不仅仅要保存IAsyncResult对象,还需要保存该委托对象,而且你还得传送过去。 还好.NET是那么的机智,有System.Runtime.Remoting.Messaging.AsyncResult类型的存在。

  1. public class AsyncResult : IAsyncResult, IMessageSink
  2. {
  3.  
  4. #region IAsyncResult 成员
  5.  
  6. public object AsyncState
  7. {
  8. get { throw new NotImplementedException(); }
  9. }
  10.  
  11. public System.Threading.WaitHandle AsyncWaitHandle
  12. {
  13. get { throw new NotImplementedException(); }
  14. }
  15.  
  16. public bool CompletedSynchronously
  17. {
  18. get { throw new NotImplementedException(); }
  19. }
  20.  
  21. public bool IsCompleted
  22. {
  23. get { throw new NotImplementedException(); }
  24. }
  25.  
  26. #endregion
  27.  
  28. public bool EndInvokeCalled { get; set; }
  29.  
  30. public virtual object AsyncDelegate { get; }
  31.  
  32. //IMessageSink 成员
  33. }

看着上面有个AsyncDelegate的属性,会不会觉得很漂亮,不错,它就是原始发起委托的引用,看下如何使用AsyncDelegate来使用EndInvoke():

  1. public class OperationTest
  2. {
  3.  
  4. public void Test()
  5. {
  6. Operation operation = new Operation();
  7. OperationDelegate Additiondelegate = operation.Addition;
  8. int Result;
  9. Result = GetResult(Additiondelegate.BeginInvoke(, , null, null));
  10. }
  11.  
  12. private int GetResult(IAsyncResult asyncresult)
  13. {
  14. AsyncResult asyncResult = (AsyncResult)asyncresult;
  15. OperationDelegate operationdelegate = asyncResult.AsyncDelegate as
  16.  
  17. OperationDelegate;
  18. if (operationdelegate != null)
  19. {
  20. Debug.Assert(asyncResult.EndInvokeCalled == false);//EndInvoke()是否被调用过
  21. return operationdelegate.EndInvoke(asyncResult);
  22. }
  23. return -;
  24. }
  25. }

3.4 轮循或等待

看到这里,善于思考的朋友会发现,还存在着一个很大的问题,就是发起异步调用的客户端,如何知道自己 的异步函数是否执行完毕了?或者是想等待一会,做一些其他的处理,然后再继续等待,该怎么来实现呢?

从BeginInvoke()返回的IAsyncResult接口有个AsyncWaitHandle属性,它是干吗的呢?就把它理解为消息接收器吧。

  1. Operation operation = new Operation();
  2. OperationDelegate Additiondelegate = operation.Addition;
  3. IAsyncResult asyncResult = Additiondelegate.BeginInvoke(, , null, null);
  4. asyncResult.AsyncWaitHandle.WaitOne();//如果任务完成则不会阻塞 否则阻塞当前线程
  5. int Result;
  6. Result = Additiondelegate.EndInvoke(asyncResult);
  7. Debug.Assert(Result == );

代码和3.2的几乎相同,区别就是这段代码保证了EndInvoke()的调用者不会被阻塞。

看一下等待一下,如果没完成处理其他任务,回来再等待是怎么实现的。

  1. Operation operation = new Operation();
  2. OperationDelegate Additiondelegate = operation.Addition;
  3. IAsyncResult asyncResult = Additiondelegate.BeginInvoke(, , null, null);
  4. while (asyncResult.IsCompleted == false)//判断异步任务是否完成
  5. {
  6. asyncResult.AsyncWaitHandle.WaitOne(,false);//如果任务完成则不会阻塞 否则阻塞当前线程10毫秒
  7. //这里做一些其他操作
  8. }
  9. int Result;
  10. Result = Additiondelegate.EndInvoke(asyncResult);
  11. Debug.Assert(Result == );

3.5 使用回调函数

现在我们要来说说BeginInvoke()的第三个参数了, public delegate void AsyncCallback(IAsyncResult ar);

第三个参数就是系统提供的一个委托类型,委托签名也都看到了。 使用回调函数的好处就是不需要去处理等待操作了,因为在异步任务完成的时候, 会调用你传给BeginInvoke()里AsyncCallback委托所关联的目标方法。

  1. public class OperationTest
  2. {
  3.  
  4. public void Test()
  5. {
  6. Operation operation = new Operation();
  7. OperationDelegate Additiondelegate = operation.Addition;
  8.  
  9. Additiondelegate.BeginInvoke(, , new AsyncCallback(OnCallBack), null);
  10. }
  11.  
  12. private void OnCallBack(IAsyncResult asyncresult)
  13. {
  14. AsyncResult asyncResult = (AsyncResult)asyncresult;
  15. OperationDelegate operationdelegate = asyncResult.AsyncDelegate as
  16.  
  17. OperationDelegate;
  18. if (operationdelegate != null)
  19. {
  20. Debug.Assert(asyncResult.EndInvokeCalled == false);
  21. int result=operationdelegate.EndInvoke(asyncResult);
  22. Console.WriteLine("Operation returned" + result.ToString());
  23. }
  24. }
  25. }

这里需要说的是在异步任务完成时,执行的回调函数依然是在子线程当中,并不是在主线程中执行回调函数的。

题外话:最常见的就是在Winform开发中,Form中发起异步调用,然后回调函数操作Form中的控件或者是

值的时候就会报错, 就是这个原因,因为它们不在一个线程也不在一个上下文中,基于.NET安全策略这种操作是不允许的。

END

作者:金源

出处:http://www.cnblogs.com/jin-yuan/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面

.Net组件程序设计之异步调用的更多相关文章

  1. .Net组件程序设计之远程调用(二)

    .Net组件程序设计之远程调用(二) 激活模式 引用封送对象激活类型两种, 一种是客户端激活类型,一种是服务器端激活. 客户端激活对象 客户端激活方式:当客户端创建一个远程对象时,客户端得到的是一个新 ...

  2. .Net组件程序设计之远程调用(一)

    .Net组件程序设计之远程调用(一) 1应用程序域 我们知道我们写的C#代码是在操作系统逻辑体系结构中最上层的,然而操作系统本身是不会认识C#代码的,它只认识机器代码.那我们写的程序经过编译后是编译成 ...

  3. .NET组件程序设计之线程、并发管理(二)

    .Net组件程序设计之线程.并发管理(二) 2.同步线程 手动同步 监视器 互斥 可等待事件 同步线程 所有的.NET组件都支持在多线程的环境中运行,可以被多个线程并发访问,如果没有线程同步,这样的后 ...

  4. .Net组件程序设计之线程、并发管理(一)

    .Net组件程序设计之线程.并发管理(一) 1.线程 线程 线程的创建 线程的阻塞 线程挂起 线程睡眠 加入线程 线程中止 现在几乎所有的应用程序都是多线程的,给用户看来就是一个应用程序界面(应用程序 ...

  5. .Net组件程序设计之序列化

     .Net组件程序设计之序列化 自动序列化 本篇给大家讲解一下在.NET中的序列化,它的用处非常之多,特别是对于某种环境下保存某种状态是很好的方法,接下来就来看一下吧. Serializable属性 ...

  6. .Net组件程序设计之上下文

    .Net组件程序设计之上下文 在后续篇幅的远程调用的文章里有说到应用程序域,那是大粒度的控制程序集的逻辑存在,那么想对对象的控制又由谁来做主呢?没错了,就是上下文.CLR把应用程序域更细化了,在应用程 ...

  7. .Net组件程序设计之对象生命周期

    .Net组件程序设计之对象生命周期 .NET 垃圾回收 IDisposable() Using语句 .NET 垃圾回收 是CLR管理着垃圾回收器,垃圾回收器监控着托管堆,而我们使用的对象以及系统启动是 ...

  8. .Net组件程序设计

    .Net组件程序设计之上下文 在后续篇幅的远程调用的文章里有说到应用程序域,那是大粒度的控制程序集的逻辑存在,那么想对对象的控制又由谁来做主呢?没错了,就是上下文.CLR把应用程序域更细化了,在应用程 ...

  9. SpringBoot中异步请求和异步调用(看这一篇就够了)

    原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10661591.html,否则将追究法律责任!!! 一.SpringBoot中异步请求的使用 ...

随机推荐

  1. Startcom SSL证书申请 IIS设置 配置 攻略

    申请具体参考:http://www.cnblogs.com/yibinboy/p/6137426.html 制作要导入服务器IIS上的证书. 点击控制面板的左上角的TOOL BOX,然后点击Creat ...

  2. OS X 下不通过Homebrew安装ASP.NET 5开发环境

    在 ASP.NET 的 Home repo 里,推荐使用 Homebrew 安装开发环境,不过我的电脑里已经有 ports 了,这应该是当年用 rvm 安装 Ruby 时悄悄地装上的吧.不管怎样,作为 ...

  3. python+Eclipse+pydev环境搭建

    python+Eclipse+pydev环境搭建   本文重点介绍使用Eclipse+pydev插件来写Python代码,  以及在Mac上配置Eclipse+Pydev 和Windows配置Ecli ...

  4. 代码在ie9中不能正确执行

    <!DOCTYPE html> <html> <head lang="zh"> <meta charset="UTF-8&quo ...

  5. CustomUI Direct3D9_Sample

    刚开始建这个项目的时候编译器报了很多Link2019的错误. 后来添加了一些lib文件才解决,参考    缺少.lib文件导致的Link2019 解决方案汇总 ==================== ...

  6. RStudio技巧02_Extract Function

    RStudio 可以在 source 编辑器中分析一组选择的代码,并自动将其转化成再次使用的函数.任何选择中的"free"变量( 选择引用对象但不创建)将转化为函数参数. (也可使 ...

  7. haha2

    # YOU - fhasd - fdks jf > jd sfkjd sf ```python print "helloworld" ``` 来自为知笔记(Wiz)

  8. JavaScript解惑记之Array.prototype.sort()

    前言 看JS红宝书的5.2.5章节关于sort()方法,如何用一个compare函数,让数组顺序,倒序,有点云里雾里的.在网上度娘了一下,发现更迷糊了.走投无路的情况下,只能发动神技能,去 stack ...

  9. Redis安装配置(Windows版)

    近期项目中引入Redis,故记录下来,方便日后查看. 可参考(http://www.cnblogs.com/happyday56/p/3916388.html)不说废话,直奔主题. 一.安装前的准备: ...

  10. linux进程后台运行及输出重定向

    本机环境为ubuntu 14.04 以ping www.baidu.com为例: 1.程序的前台运行 ping www.baidu.com 可以看到,屏幕上输出了baidu返回的结果 2.实现程序后台 ...