.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组件程序设计之异步调用的更多相关文章

  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. Python 判断变量的类型

    这里有两种方法.type 和isinstance import types aaa = 0 print type(aaa) if type(aaa) is types.IntType: print & ...

  2. windows下mysql客户端输入中文显示??解决方法

    >>>>>>>>>>>>>>>>>>>> 1.检查并修改mysql的my.ini ...

  3. PDA手持终端扫描条码开单打印一体 结合后台电脑系统 数据同步交互解决方案

    PDA通过扫描商品条码移动开单,实现便携式办公,伴随式销售,PDA能通过WIFI无线局域网.GPRS互联网直接与主机连接,让公司业务人员能随时随地了解公司产品信息,直接扫描商品条码,进行开单.入库.库 ...

  4. PHP 显示文章发布日期 一小时前 一天前 一月前 一年前

    <?PHP /*** 传入日期格式或时间戳格式时间,返回与当前时间的差距,如1分钟前,2小时前,5月前,3年前等* @param string or int $date 分两种日期格式" ...

  5. 基于dubbo的分布式项目实例应用

    本文主要学习dubbo服务的启动检查.集群容错.服务均衡.线程模型.直连提供者.只定阅.只注册等知识点,希望通过实例演示进一步理解和掌握这些知识点. 启动检查 Dubbo缺省会在启动消费者时检查依赖的 ...

  6. spark scala学习笔记

    搞清楚几个概念: 闭包 柯里化 搭建了intellij idea 的scala 开发环境

  7. 【系统篇】从int 3探索Windows应用程序调试原理

    探索调试器下断点的原理 在Windows上做开发的程序猿们都知道,x86架构处理器有一条特殊的指令——int 3,也就是机器码0xCC,用于调试所用,当程序执行到int 3的时候会中断到调试器,如果程 ...

  8. bootstrap之伪元素

    bootstrap之伪元素 参考地址:http://www.cnblogs.com/keyi/p/5943178.html http://www.runoob.com/css/css-pseudo-e ...

  9. 关于UIAlertAction如何修改sheet上的字体颜色

    相信很多程序员都会遇到需求是这样的: 但是你发现无论怎么设置cancel和Destructive都无法让红色字体移动到下面取消按钮上: 其实之前一直用错,用了ios9之前的UIActionSheet这 ...

  10. SQL 提高查询效率

    1.关于SQL查询效率,100w数据,查询只要1秒,与您分享: 机器情况p4: 2.4内存: 1 Gos: windows 2003数据库: ms sql server 2000目的: 查询性能测试, ...