C#语言中的异步方法调用

 

.NET框架基类库(BCL)中有好几种类都可以提供同步和异步的方法调用。不过,因为同步方法调用会导致程序流程中途等待,所以采用同步方法的情况下往往会导致程序执行的延迟,相比来说,在某些条件下选择异步方法调用就可能更好一些,例如,有的时候程序需要给多个Web服务发出请求,这时就最好采用异步方法。这篇文章的主旨就是向读者阐述如何用C#在.NET开发中采取异步方法调用。

异步和同步

同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果;而异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作。

为了说明异步方法调用所具有的优点,现在就让我们首先看一个例子,在这个例子中采用同步解决方案并不是最佳选择。这个例子要用到System.Net.Dns类。

同步版本的Resolve方法代码是如下定义的:

public static IPHostEntry Resolve(
string hostName
);

Resolve方法只接受一个参数hostName,hostName既可以是DNS名字(比如www.mydomain.com)也可以是点分十进制格式的IP地址(例如10.10.14.2)。Resolve方法只返回一个IPHostEntry对象。你可以通过IPHostEntry对象的成员来检索主机名、IP地址以及其他同特定主机有关的信息。

你能在自己的程序中使用Resolve同步方法,代码如下:

IPHostEntry host = Dns.Resolve(“10.10.14.2”);
Console.WriteLine(host.HostName);

这里有个问题,一旦调用了Dns.Resolve,你的程序就会被阻塞执行直到Resolve方法完成其名称解析任务并且返回一个IPHostEntry对象,这个过程可能会用去若干秒的时间。由于DNS 解析涉及到网络访问,所以这种方法调用完全可能受到包括网络延迟在内得推迟操作完成的因素的影响,所以在以上的情况下更适合于采用异步方法来获得同样的结果。

异步设计

部件之间的异步通讯要采取以下的设计模式:程序必须能确定被调用方法已经完成任务的时刻或者能对其操作状态进行判断。这种设计模式允许程序获得方法返回的任何结果。所以说,在用到Web服务的时候,因为你的程序能在程序继续执行之前给多个Web服务同时发送请求然后等待各个方法返回各自的结果,所以异步设计在以上情况下特别有用。

在这种情形下,具有不同程度延迟的网络、各类公司通过因特网等等都可以提供程序用到的Web服务。因而,完成请求所需要的时间长度只受程序控制以外的若干因素影响。

在 .NET 框架内采用异步设计模式通常具有以下的特点,那就是要用到BeginXXXX和EndXXXX字样的方法,XXXX 在此表示该方法的同步版本的名字。现在就让我们仔细考察一下异步方法调用。

BeginResolve方法和IAsyncResult

为了不阻塞程序的执行(就像第一个例子那样),你可以选择使用Resolve方法的异步版本:BeginResolve。这个方法的用法如下:

public static IAsyncResultBeginResolve(
string hostName,
AsyncCallbackrequestCallback,
object stateObject
);

BeginResolve方法和其同步版本一样接受同样的参数hostName,不过增加了异步设计模式下所需要的另两个参数:requestCallback和stateObject参数。我们很快就会讨论到这两个参数,但是首先让我们注意一下返回值:一个IAsyncResult接口。

当你异步调用某个方法的时候,在被调用方法有机会完成任务以前(甚至有时在刚调用的时候)方法调用就会很快地返回程序。按照定义,BeginResolve方法不会返回IPHostEntry对象,相反,它会返回一个等待对象,这就是IAsyncResult接口,之后可以使用它来检索方法调用的结果。

IAsyncResult 接口的定义如下:
public interface IAsyncResult {
object AsyncState
WaitHandleAsyncWaitHandle
boolCompletedSynchronously
boolIsCompleted
}

该接口的第一个属性是AsyncState,它会返回传递给BeginResolve方法的stateObject参数同样的对象。这个参数值没有受到任何限制。它可以是程序想用来追踪这个特定方法调用的任何东西。被调用方法无论在什么方式下都不会使用或操作它。

IAsyncResult接口的第二个属性是AsyncWaitHandle,你的程序可以用它来等待方法完成其任务,具体做法是把它传递给WaitHandle类的方法WaitAll、WaitOne或WaitAny之一即可。如果你正在平行发送若干异步方法调用、希望它们都能保证在继续你自己程序的工作之前完成,那么这个属性就会非常管用,如果程序的继续操作依赖于以上这些调用中的一个或者多个结果,这个属性的用处就更大了。

该接口的第3个属性是CompletedSynchronously,它返回一个布尔值,表示方法是否能在BeginResolve方法返回的时候完成任务。

第4个属性是IsCompleted,它也返回一个布尔值,表示方法所要完成的任务是否已经完成了。如果你正在使用一种查询机制来确定异步调用是否完成其任务,那么这个属性此刻就能派上用场了。

实际上,发起和完成.NET异步调用有4种方案可供你选择。首先是采用查询(IsCompleted属性),其次是利用回调函数(在下面讨论),第三是采用AsyncWaitHandle来等待方法调用的完成,最后,你还可以自己调用EndXXXX,当BeginXXXX方法返回IAsyncResult之时你且尽管等着调用完成即可。这最后两种技术之间的差别在于,如果你采取等待自己(使用AsyncWaitHandle)这一措施,那么你能在一定超时设置基础之上随时“唤醒”,同时在这一时刻决定你是否确实想再等待下去。

EndResolve方法和AsyncCallback

完成异步方法调用的方法之一是给BeginXXXX方法提供一个AsyncCallback函数。这个函数的语法如下:

public delegate void AsyncCallback(
       IAsyncResultar
);

在程序中加入以上的代码同时创建一个AsyncCallback函数指向该方法,你就可以让异步方法调用通知你的程序它在什么时候能完成了自己的任务处理。下列代码片段说明该如何采用回调函数调用Dns类的BeginResolve方法:
AsyncCallback callback = new AsyncCallback(GetResult);
IAsyncResultar = Dns.BeginResolve("10.10.14.2", callback, null);
// 以上任务完成之时尽管做其他工作。


当BeginResolve方法完成任务并且返回一个IPHostEntry对象的时候,它会调用传递给它的回调函数让我们获得结果。下列代码显示了程序中实现的GetResult 方法:

private void GetResult(IAsyncResultar) {
IPHostEntry host = Dns.EndResolve(ar);
}


在得到了解析请求的IP地址后,Dns类调用GetResult方法,把最初调用所返回的同样IAsyncResult接口传递给BeginResolve。这样我们就可以用该参数调用EndResolve来获得调用结果。

总结

在.NET下你既可以使用同步也可以使用异步方法,这完全是你的编程自由,不过,在很多情况下采用异步途径往往更有效率。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows 应用程序编程常见的一个模式就是,在GUI用户界面下,将耗时的文件和网络处理放入 子线程,以避免用户界面不能响应的问题。在.NET出现以前,创建线 程并监视线程结束,还要更新 界面等工作,即复杂又要手写大量代码,并且难以调试。在.NET,程序员可以通过异步调用,使 用简单的代码完成这项工作。 .NET Framework允许异步调用任何方法。使用异步调用之前,要定义一个委托,它的方法签名要与 调用方法签名一致。.NET会自动产生此委托的BeginInvoke和EndInvoke方法。 BeginInvoke 方法用于启动异步调用。它除了具有调用方法相同的参数外,还附加了两个额外的参 数,这两个额外参数将用于回调方法。 BeginInvoke执行后立即返回,并不等待异步调用完成。 BeginInvoke返回一个IAsyncResult接口,它可以用于监视调用的进度。EndInvoke方法被用来获取 异步调用的结果。在 BeginInvoke执行以后,任何时间都可以调用EndInvoke。如果异步调用尚未完 成,EndInvoke会被阻塞,直到异步调用完成。EndInvoke带有一个IAsyncResult接口类型的参数, 并返回一个IAsyncResult接口,用来获取调用结果。 本文的代码演示了使用BeginInvoke和EndInvoke进行异步调用的四种常见调用方式。在调用 BeginInvoke之后,您可以: 1.完成一些其它工作,然后调用EndInvoke,等待异步调用完成。 2.使用IAsyncResult.AsyncWaitHandle获取一个WaitHandle,使用它的WaitOne方法执行阻塞调 用,完成后再调用EndInvoke。 3.轮询BeginInvoke返回的IAsyncResult接口,在异步调用完成后,再调用EndInvoke。 4.给BeginInvoke传递一个回调方法的委托,异步调用完成后,这个回调方法会在ThreadPool线程 上执行,可以在这个方法调用EndInvoke。 警告:异步调用完成后,必须调用EndInvoke。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值