随着多核处理器的普及,异步通信已成为提升应用程序性能和响应性的关键技术。自Silverlight引入以来,异步通信变得必不可少,因为所有Web请求在该运行时中都是异步发生的。Windows 8进一步将这一趋势推向更基础的层面,许多系统调用都是异步进行的。
实现异步通信要求开发者具备额外的技能,因为他们在一定程度上失去了对操作顺序的控制,这反过来又导致了分散但仍然相关的代码片段,这与某些面向对象的编程范式相矛盾。
本文描述了一些简化异步通信处理的简单方法。相关库提供了几种工具,具有以下优势:
要异步执行操作,库提供了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使用。它管理以下方面:
本文不描述错误处理,因为它不是其主要主题,超出了其范围。然而,有许多示例说明如何实现这一点,例如在“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"
) );
}