异步通信的简化实现

随着多核处理器的普及,异步通信已成为提升应用程序性能和响应性的关键技术。自Silverlight引入以来,异步通信变得必不可少,因为所有Web请求在该运行时中都是异步发生的。Windows 8进一步将这一趋势推向更基础的层面,许多系统调用都是异步进行的。

实现异步通信要求开发者具备额外的技能,因为他们在一定程度上失去了对操作顺序的控制,这反过来又导致了分散但仍然相关的代码片段,这与某些面向对象的编程范式相矛盾。

本文描述了一些简化异步通信处理的简单方法。相关库提供了几种工具,具有以下优势:

  • 统一操作序列的4个区域:Main、OnSuccess、OnError、OnFinally
  • 组合操作序列的代码
  • 通过使用泛型,返回值和参数的类型安全性
  • 自动重用函数的返回值
  • 简化错误和异常处理
  • 将异步通信的核心集中于一处,便于分析、记录等

异步操作

要异步执行操作,库提供了AsyncAction类,该类提供了各种实用方法来启动它。最详细的签名定义如下:

C# public static void Start( Invocable main, Invocable onSuccess, InvocableAction1Base<Exception> onError, Invocable onFinally, AsyncContext context )

参数main、onSuccess、onError、onFinally代表上述操作序列图的代码块。如果Main执行无误,则启动OnSuccess块。否则,将产生的Exception作为参数传递给OnError。使用AsyncAction时,必须给定Main动作。AsyncContext封装了执行上下文,稍后详细描述。

以下示例展示了在Main动作之外使用OnSuccess动作:

C# public void DeleteAllCustomersMinimal() { AsyncAction.Start( new InvocableAction0( Services.DeleteAllCustomers ), new InvocableAction0( () => Customers.Clear() ) ); }

Actions有多种变体,最多可带4个参数:InvocableAction0 – InvocableAction4。

给定这个继承层次结构,可以根据需要单独定制对相应动作的调用。以下示例演示了如何使用InvocableAction2为OnError动作添加额外的Customer参数:

C# public void DeleteCustomer( Customer customer ) { AsyncAction.Start( // main new InvocableAction1<int>( Services.DeleteCustomer, customer.Id ), // on success new InvocableAction1<Customer>( c => Customers.Remove( c ), customer ), // on error new InvocableAction2<Exception, Customer>( ( e, c ) => ShowError( e + " - " + c ), customer ), // on finally new InvocableAction1<Customer>( c => Debug.WriteLine( " DeleteCustomer: " + c ) ) ); }

异步函数

可以通过AsyncFunction类的start方法的不同变体启动异步函数。所有执行参数的最详细签名定义如下:

C# public static void Start( InvocableFunction<TResult> main, InvocableAction1Base<TResult> onSuccess, InvocableAction1Base<Exception> onError, Invocable onFinally, AsyncContext context )

参数main、onSuccess、onError、onFinally代表上述操作序列图的代码块。与AsyncAction不同,参数Main是InvocableFunction类型,因此返回结果。如果Main执行无误,则启动OnSuccess块。Main函数的返回值作为其第一个参数传递给OnSuccess动作。如果执行Main函数导致异常,则将其作为参数传递给OnError动作。使用AsyncFunction时,必须给定Main函数和相应的OnSuccess动作。AsyncContext封装了执行上下文,稍后详细描述。

以下示例演示了如何将Main函数的结果传递给OnSuccess动作:

C# public void GetCustomerSalesVolumeMinimal( Customer customer ) { AsyncFunction<double>.Start( // main new InvocableFunction1<int, double>( Services.GetCustomerSalesVolume, customer.Id ), // on success new InvocableAction2<double, Customer>( ( sv, c ) => c.SalesVolume = sv, customer ) ); }

适合Main参数的AsyncFunction可以是最多4个参数的函数:InvocableFunction0 – InvocableFunction4。

执行上下文

异步通信的核心是AsyncContext类,它由AsyncAction和AsyncFunction使用。它管理以下方面:

  • Dispatcher:用于实际运行异步操作或函数的Dispatcher。默认情况下,它将使用当前线程的Dispatcher。
  • Thread:给定属性ShouldInvokeOnDispatcherThread,可以决定Dispatcher是否应该在当前Thread中运行,还是通过ThreadPool在单独的Thread中运行。
  • Invoke:实现中央操作序列,实际执行异步操作或函数。
  • Error handling:对于异步Web通信(Silverlight)中服务器端错误的适当处理,需要进一步的步骤。需要封装在服务器上发生的错误,并通过WCF将它们传输到客户端。

本文不描述错误处理,因为它不是其主要主题,超出了其范围。然而,有许多示例说明如何实现这一点,例如在“Catch and Handle WCF Service Exceptions in Silverlight”中。

应用示例

附带的WPF、Silverlight和Windows Phone的示例应用程序演示了如何异步加载客户信息项列表:

C# private void LoadCustomers() { AsyncFunction<CustomerCollection>.Start( // main new InvocableFunction0<CustomerCollection>( services.LoadCustomers ), // on success new InvocableAction1<CustomerCollection>( OnCustomersLoaded ), // on error new InvocableAction1<Exception>( OnCustomerLoadError ), // on finally new InvocableAction1<string>( OnFinished, " LoadCustomers" ) ); }

在客户被加载后,它们会显示在列表中。在该列表中选择一个客户将显示其数据在详细信息部分。此外,该客户的销售额异步从外部服务获取:

C# private void UpdateCustomerInfo( Customer customer ) { // static customer data CustomerId.Text = customer.Id.ToString( CultureInfo.InvariantCulture ); CustomerFirstName.Text = customer.FirstName; CustomerLastName.Text = customer.LastName; // sales volume AsyncFunction<double>.Start( // main new InvocableFunction1<int, double>( services.GetCustomerSalesVolume, customer.Id ), // on success new InvocableAction2<double, Customer>( ( salesVolume, cust ) => { cust.SalesVolume = salesVolume; CustomerSalesVoume.Text = salesVolume.ToString( " C" ); AddToHistory( string.Format( " customer {0} sales volume: {1:C}" , cust, salesVolume ) ); }, customer ), // on error new InvocableAction2<Exception, Customer>( ( e, cust ) => { CustomerSalesVoume.Text = string.Empty; AddToHistory( string.Format( " error get sales volume: {0}" , cust ) ); }, customer ), // on finally new InvocableAction1<string>( action => AddToHistory( string.Format( " '{0}' finished" , action ) ), " GetCustomerSalesVolume" ) ); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485