| |
为Windows应用创建简单的异步调用模式 |
|
时间: 2005-10-18 来自:msdn |
 |
|
简介
最近我编写了很多智能客户端应用,总结了一些能够使应用程序在后台调用Web
Service时不冻结前台界面的异步调用方法。虽然当前.NET
Framework本身已经提供了异步调用的机制,但我发现在Windows应用中这一机制比较难于把握,因为这时你需要正确的控制用户界面线程处理。
在这篇文章中,我将教给您一种在Windows应用程序中实现异步调用Web服务的简单方法,通过这一方法,您不用再考虑后台线程与前台界面线程的交互关系了。
服务代理
Visual
Studio.NET会生成较好的Web服务代理类,通过它可以异步的使用Web服务,但是这个代理类实现的是.NET
Framework本身的异步调用机制,如上所述,这一机制对于Windows应用来说并不十分方便。由于这个原因,我一般不直接使用生成的代理类,而是在中间增加服务代理类。
服务代理类就是增加了额外功能的类,这些功能可以帮助客户端程序与Web服务进行交互。服务代理类实现了许多有用的功能,包括数据缓存,安全身份管理,离线操作支持等等。本文中创建的服务代理类比.NET
Framework本身的普通代理类实现了更简便的异步调用模式。
用户界面线程
应用程序从一个创建和管理用户界面的线程起始,这一线程被称为用户界面线程。大多数开发者本能的会使用用户界面线程完成所有的工作,包括进行Web服务调用,远程对象调用,访问数据库等等,大多数使用和性能方面的问题是由这一不恰当的方法引起的。
问题的本质是你永远不可能精确的预知访问Web服务,远程对象,或者数据库所需的时间。而且当你在用户界面线程中进行这类的调用时,用户界面就有可能会产生令人恼怒的冻结。
自然而然的,你会把这一类的调用放置在一个单独的线程中,但我更进了一步,建议您把所有的非用户界面工作坊制在一个分离的线程中。我的观点是,用户界面线程只用来管理用户界面,而所有那些你不能保证良好响应时间的对象调用都应该是异步的,无论是进程内的,跨进程的,还是跨计算机的。
无论如何,尽量使用户界面线程处理的异步调用模式简单化,我已经实现了一个与Visual Studio
2005里某个特性类似的简单异步调用模式。作为开始,我们首先解释一下当前.NET
Framework中异步调用模式的工作原理。
.NET异步调用模式
系统生成的Web服务代理类的每个Web函数都有一个Begin和一个End方法,每个支持.NET
Framework异步调用模式的对象都和这个类似。开始进行异步调用时,客户端调用Begin方法时就立即响应,或者在建立了访问Web服务的独立线程后马上响应。在这之后的某个时间,当Web服务访问完成后,客户端再调用End方法。
但客户端如何知道什么时候调用End方法呢?Begin方法会返回一个IAsyncResult对象,可以帮助你跟踪异步调用的过程,也可以明确的等待后台线程完成,但如果在用户界面线程中进行这些工作,会降低整个系统的同步性。更好的方法是,在用户界面进程中注册一个回调函数,当其它工作完成时产生一个自动通知。
让我们看一段样例代码,在这段代码中,我们从一个Web服务中获取一些客户数据,这些功能通过Web服务代理类里的GetCustomerData方法完成。我们可以启动这个Web服务调用,并且用以下代码注册一个回调函数,用来在用户界面线程中产生与应用程序进行交互的功能。
private void SomeUIEvent( object sender, EventArgs e ) { //
Create a callback delegate so we will // be notified when the call has
completed. AsyncCallback callBack = new AsyncCallback(
CustomerDataCallback );
// Start retrieving the customer
data. _proxy.BeginGetCustomerData( "Joe Bloggs", callBack, null
); } | Web服务调用最终返回CustomerDataCallback方法,在这个方法中,我们需要调用真正用于获取客户数据的Web服务代理类中的End方法,这个方法可以实现如下:
public void CustomerDataCallback( IAsyncResult ar ) { //
Retrieve the customer data. _customerData = _proxy.EndGetCustomerData(
ar
); } | 现在,你必须注意,这一方法是被后台工作线程调用的。如果想在前台界面上使用刚刚获得的信息(例如在一个data
grid控件中显示那些客户数据),一定不要忘记在用户界面线程中进行更新。如果忘了这样做,就会发生很多莫名其妙的错误,而且调试起来还相当的不易。
那么我们怎么切换线程呢?我们可以使用服务代理的方法,所有的控件都源自这些对象的实现。我们可以实现一个从用户界面线程调用的方法,在这个方法内我们可以安全的更新我们的界面。使用Control.Invoke方法,我们必须给用户更新方法传递一个委托,现在,CustomerDataCallback方法的代码更新如下:
public void CustomerDataCallback( IAsyncResult ar ) { //
Retrieve the customer data. _customerData = _proxy.EndGetCustomerData(
ar );
// Create an EventHandler delegate. EventHandler
updateUI = new EventHandler( UpdateUI );
// Invoke the delegate on
the UI thread. this.Invoke( updateUI, new object[] { null, null }
); } | UpdateUI方法可以按如下办法实现:
private void UpdateUI( object sender, EventArgs e ) { // Update
the user interface. _dataGrid.DataSource =
_customerData; } | 这并不是十分非常严谨科学,因此可以使这一“两次跳转”的复杂问题简单化起来。关键在于异步方法的原始调用(以WinForm类为例)用来负责转换线程,并且需要另一个委托以及Control.Invoke方法。
|
|
|
|
|
|
|
|