来自:http://www.amuhouse.com/blog//article.asp?id=157
.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 IAsyncResult BeginResolve(
string hostName,
AsyncCallback requestCallback,
object stateObject
);
BeginResolve方法和其同步版本一样接受同样的参数hostName,不过增加了异步设计模式下所需要的另两个参数:requestCallback和stateObject参数。我们很快就会讨论到这两个参数,但是首先让我们注意一下返回值:一个IAsyncResult接口。
当你异步调用某个方法的时候,在被调用方法有机会完成任务以前(甚至有时在刚调用的时候)方法调用就会很快地返回程序。按照定义,BeginResolve方法不会返回IPHostEntry对象,相反,它会返回一个等待对象,这就是IAsyncResult接口,之后可以使用它来检索方法调用的结果。
IAsyncResult 接口的定义如下:
public interface IAsyncResult {
object AsyncState {get;}
WaitHandleAsyncWaitHandle {get;}
boolCompletedSynchronously {get;}
boolIsCompleted {get;}
}
该接口的第一个属性是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下你既可以使用同步也可以使用异步方法,这完全是你的编程自由,不过,在很多情况下采用异步途径往往更有效率。
.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 IAsyncResult BeginResolve(
string hostName,
AsyncCallback requestCallback,
object stateObject
);
BeginResolve方法和其同步版本一样接受同样的参数hostName,不过增加了异步设计模式下所需要的另两个参数:requestCallback和stateObject参数。我们很快就会讨论到这两个参数,但是首先让我们注意一下返回值:一个IAsyncResult接口。
当你异步调用某个方法的时候,在被调用方法有机会完成任务以前(甚至有时在刚调用的时候)方法调用就会很快地返回程序。按照定义,BeginResolve方法不会返回IPHostEntry对象,相反,它会返回一个等待对象,这就是IAsyncResult接口,之后可以使用它来检索方法调用的结果。
IAsyncResult 接口的定义如下:
public interface IAsyncResult {
object AsyncState {get;}
WaitHandleAsyncWaitHandle {get;}
boolCompletedSynchronously {get;}
boolIsCompleted {get;}
}
该接口的第一个属性是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下你既可以使用同步也可以使用异步方法,这完全是你的编程自由,不过,在很多情况下采用异步途径往往更有效率。