使用 Task 简化异步编程

简介:

Net 传统异步编程概述

.NET Framework 提供以下两种执行 I/O 绑定和计算绑定异步操作的标准模式:

  • 异步编程模型 (APM),在该模型中异步操作由一对 Begin/End 方法(如 FileStream.BeginRead 和 Stream.EndRead)表示。
  • 基于事件的异步模式 (EAP),在该模式中异步操作由名为“操作名称Async”和“操作名称Completed”的方法/事件对(例如 WebClient.DownloadStringAsync 和 WebClient.DownloadStringCompleted)表示。 (EAP 是在 .NET Framework 2.0 版中引入的)。

Task 的优点以及功能

通过使用 Task 对象,可以简化代码并利用以下有用的功能:

  • 在任务启动后,可以随时以任务延续的形式注册回调。
  • 通过使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,协调多个为了响应 Begin_ 方法而执行的操作。
  • 在同一 Task 对象中封装异步 I/O 绑定和计算绑定操作。
  • 监视 Task 对象的状态。
  • 使用 TaskCompletionSource 将操作的状态封送到 Task 对象。

使用 Task 封装常见的异步编程模式

1、 使用 Task 对象封装 APM 异步模式, 这种异步模式是 .Net 标准的异步模式之一, 也是 .Net 最古老的异步模式, 自 .Net 1.0 起就开始出现了,通常由一对 Begin/End 方法同时出现, 以 WebRequest 的 BeginGetResponse 与 EndGetResponse 方法为例:

1
2
3
4
5
6
7
8
9
var  request = WebRequest.CreateHttp(UrlToTest);
request.Method = "GET" ;
var  requestTask = Task.Factory.FromAsync<WebResponse>(
    request.BeginGetResponse,
    request.EndGetResponse,
    null
);
requestTask.Wait();
var  response = requestTask.Result;

2、使用 Task 对象封装 EPM 异步模式, 这种模式从 .Net 2.0 开始出现, 同时在 Silverlight 中大量出现, 这种异步模式以 “操作名称Async” 函数和 “操作名称Completed” 事件成对出现为特征, 以 WebClient 的 DownloadStringAsync 方法与 DownLoadStringCompleted 事件为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var  source = new  TaskCompletionSource< string >();
var  webClient = new  WebClient();
webClient.DownloadStringCompleted += (sender, args) => {
    if  (args.Cancelled) {
       source.SetCanceled();
       return ;
    }
    if  (args.Error != null ) {
       source.SetException(args.Error);
       return ;
    }
    source.SetResult(args.Result);
};
webClient.DownloadStringAsync( new  Uri(UrlToTest, UriKind.Absolute), null );
source.Task.Wait();
var  result = source.Task.Result;

3、 使用 Task 对象封装其它非标准异步模式, 这种模式大量出现在第三方类库中, 通常通过一个 Action 参数进行回调, 以下面的方法为例:

1
void  AddAsync( int  a, int  b, Action< int > callback)

封装方法与封装 EPM 异步模式类似:

1
2
3
4
5
var  source = new  TaskCompletionSource< int >();
Action< int > callback = i => source.SetResult(i);
AddAsync(1, 2, callback);
source.Task.Wait();
var  result = source.Task.Result;

通过上面的例子可以看出, 用 Task 对象对异步操作进行封装之后, 异步操作简化了很多, 只要调用 Task 的 Wait 方法, 可以直接获取异步操作的结果, 而不用转到回调函数中进行处理, 接下来看一个比较实际的例子。

缓冲查询示例

以 Esri 提供的缓冲查询为例, 用户现在地图上选择一个合适的点, 按照一定半径查询查询缓冲区, 再查询这个缓冲区内相关的建筑物信息, 这个例子中, 我们需要与服务端进行两次交互:

  1. 根据用户选择的点查询出缓冲区;
  2. 查询缓冲区内的建筑物信息;

这个例子在 GIS 查询中可以说是非常简单的, 也是很典型的, ESRI 的例子中也给出了完整的源代码, 这个例子的核心逻辑代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
_geometryService = new  GeometryService(GeoServerUrl);
_geometryService.BufferCompleted += GeometryService_BufferCompleted;
 
_queryTask = new  QueryTask(QueryTaskUrl);
_queryTask.ExecuteCompleted += QueryTask_ExecuteCompleted;
 
void  MyMap_MouseClick( object  sender, Map.MouseEventArgs e) {
    // 部分代码省略, 开始缓冲查询
    _geometryService.BufferAsync(bufferParams);
 
}
 
void  GeometryService_BufferCompleted( object  sender, GraphicsEventArgs args) {
    // 部分代码省略, 获取缓冲查询结果, 开始查询缓冲区内的建筑物信息
    _queryTask.ExecuteAsync(query);
}
 
void  QueryTask_ExecuteCompleted( object  sender, QueryEventArgs args) {
    // 将查询结果更新到界面上
}

这只是一个 GIS 开发中很简单的一个查询, 上面的代码却将逻辑分散在三个函数中, 在实际应用中, 与服务端的交互次数会更多, 代码的逻辑会分散在更多的函数中, 导致代码的可读性以及可维护性降低。 如果使用 Task 对象对这些任务进行封装, 那么整个逻辑将会简洁很多, GeometryService 和 QueryTask 提供的是 EPM 异步模式, 相应的封装方法如上所示, 最后, 用 Task 封装异步操作之后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void  MyMap_MouseClick( object  sender, Map.MouseEventArgs e) {
    Task.Factory.StartNew(() => {
       // 省略部分 UI 代码, 开始缓冲查询
       var  bufferParams = new  BufferParameters() { /* 初始化缓冲查询参数 */ };
       var  bufferTask = _geometryService.CreateBufferTask()
       // 等待缓冲查询结果
       bufferTask.Wait();
       // 省略更新 UI 的代码, 开始查询缓冲区内的建筑物信息
       var  query = new  Query() { /* 初始化查询参数 */  };
       var  queryExecTask = _queryTask.CreateExecTask(query);
       queryExecTask.Wait();
       // 将查询结果显示在界面上, 代码省略
    });
}

从上面的代码可以看出, 使用 Task 对象可以把原本分散在三个函数中的逻辑集中在一个函数中即可完成, 代码的可读性、可维护性比原来增加了很多。

Task 能完成的任务远不止这些,比如并行计算、 协调多个并发任务等, 有兴趣的可以进一步阅读相关的 MSDN 资料

张志敏所有文章遵循创作共用版权协议,要求署名、非商业 、保持一致。在满足创作共用版权协议的基础上可以转载,但请以超链接形式注明出处。

本博客已经迁移到 GitHub , 围观地址: http://beginor.github.io/

本文转自张志敏博客园博客,原文链接:http://www.cnblogs.com/beginor/archive/2012/01/16/2323265.html ,如需转载请自行联系原作者
相关文章
|
5月前
|
C# 开发者
深入理解C#中的`Task<T>`:异步编程的核心
【1月更文挑战第3天】本文旨在探讨C#中`Task<T>`的使用和理解,作为异步编程模式的核心组件。`Task<T>`允许开发者在不阻塞主线程的情况下执行异步操作,并返回一个指定类型`T`的结果。通过定义返回`Task<T>`的异步方法、使用`async`和`await`关键字、处理异常以及获取任务结果,开发者可以编写出高效且响应迅速的应用程序。此外,本文还介绍了如何配置任务以及实现任务的连续性和组合,为掌握C#中的异步编程提供了全面的指导。
|
5月前
|
缓存 Java 调度
Java并发编程学习10-任务执行与Executor框架
【4月更文挑战第12天】本篇 重点讲解任务执行和 Executor框架的基础知识
56 4
Java并发编程学习10-任务执行与Executor框架
|
12月前
|
XML Java 数据格式
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇
64 0
|
12月前
|
Java Spring
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇2
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇2
109 0
|
5月前
|
存储 关系型数据库 MySQL
纯c协程框架NtyCo实现与原理
纯c协程框架NtyCo实现与原理
133 1
|
12月前
|
并行计算 Java
【Future&ForkJoin框架原理】
【Future&ForkJoin框架原理】
|
Java 开发者 Spring
异步编程利器:深入了解 @Async 注解
在现代的应用程序开发中,高并发和响应速度是至关重要的。为了在处理多个任务时提高效率,异步编程成为了一个重要的技术。Spring 框架提供了 `@Async` 注解,用于简化异步编程,使开发者能够更轻松地处理并发任务。在本文中,我们将详细介绍 `@Async` 注解的特性、用法以及在实际应用中的优势。
122 0
|
消息中间件 Java 数据库
实现异步编程的方式
实现异步编程的方式
Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现
普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法。
Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现
|
5月前
|
弹性计算 运维 Serverless
基于函数计算搭建的异步任务执行框架
本文介绍基于函数计算实现的异步任务执行框架(编程语言:Python3),把跟阿里云资源开通相关的API封装到一个独立的模块,提供标准的API跟企业内部在用的ITSM或OA进行集成,降低客户对接API门槛,更快上阿里云。
130 0
基于函数计算搭建的异步任务执行框架