當(dāng)前位置:首頁(yè)文章首頁(yè) 新聞中心

使用 Task 簡(jiǎn)化異步編程

作者:  來(lái)源:  發(fā)布時(shí)間:2012-1-16 10:12:06  點(diǎn)擊:

.Net 傳統(tǒng)異步編程概述

.NET Framework 提供以下兩種執(zhí)行 I/O 綁定和計(jì)算綁定異步操作的標(biāo)準(zhǔn)模式:

  • 異步編程模型 (APM),在該模型中異步操作由一對(duì) Begin/End 方法(如 FileStream.BeginRead 和 Stream.EndRead)表示。
  • 基于事件的異步模式 (EAP),在該模式中異步操作由名為“操作名稱(chēng)Async”和“操作名稱(chēng)Completed”的方法/事件對(duì)(例如 WebClient.DownloadStringAsync 和 WebClient.DownloadStringCompleted)表示。 (EAP 是在 .NET Framework 2.0 版中引入的)。

Task 的優(yōu)點(diǎn)以及功能

通過(guò)使用 Task 對(duì)象,可以簡(jiǎn)化代碼并利用以下有用的功能:

  • 在任務(wù)啟動(dòng)后,可以隨時(shí)以任務(wù)延續(xù)的形式注冊(cè)回調(diào)。
  • 通過(guò)使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,協(xié)調(diào)多個(gè)為了響應(yīng) Begin_ 方法而執(zhí)行的操作。
  • 在同一 Task 對(duì)象中封裝異步 I/O 綁定和計(jì)算綁定操作。
  • 監(jiān)視 Task 對(duì)象的狀態(tài)。
  • 使用 TaskCompletionSource 將操作的狀態(tài)封送到 Task 對(duì)象。

使用 Task 封裝常見(jiàn)的異步編程模式

1、 使用 Task 對(duì)象封裝 APM 異步模式, 這種異步模式是 .Net 標(biāo)準(zhǔn)的異步模式之一, 也是 .Net 最古老的異步模式, 自 .Net 1.0 起就開(kāi)始出現(xiàn)了,通常由一對(duì) Begin/End 方法同時(shí)出現(xiàn), 以 WebRequest 的 BeginGetResponse 與 EndGetResponse 方法為例:

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 對(duì)象封裝 EPM 異步模式, 這種模式從 .Net 2.0 開(kāi)始出現(xiàn), 同時(shí)在 Silverlight 中大量出現(xiàn), 這種異步模式以 “操作名稱(chēng)Async” 函數(shù)和 “操作名稱(chēng)Completed” 事件成對(duì)出現(xiàn)為特征, 以 WebClient 的 DownloadStringAsync 方法與 DownLoadStringCompleted 事件為例:

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 對(duì)象封裝其它非標(biāo)準(zhǔn)異步模式, 這種模式大量出現(xiàn)在第三方類(lèi)庫(kù)中, 通常通過(guò)一個(gè) Action 參數(shù)進(jìn)行回調(diào), 以下面的方法為例:

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

封裝方法與封裝 EPM 異步模式類(lèi)似:

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

通過(guò)上面的例子可以看出, 用 Task 對(duì)象對(duì)異步操作進(jìn)行封裝之后, 異步操作簡(jiǎn)化了很多, 只要調(diào)用 Task 的 Wait 方法, 可以直接獲取異步操作的結(jié)果, 而不用轉(zhuǎn)到回調(diào)函數(shù)中進(jìn)行處理, 接下來(lái)看一個(gè)比較實(shí)際的例子。

緩沖查詢(xún)示例

以 Esri 提供的緩沖查詢(xún)?yōu)槔?用戶(hù)現(xiàn)在地圖上選擇一個(gè)合適的點(diǎn), 按照一定半徑查詢(xún)查詢(xún)緩沖區(qū), 再查詢(xún)這個(gè)緩沖區(qū)內(nèi)相關(guān)的建筑物信息, 這個(gè)例子中, 我們需要與服務(wù)端進(jìn)行兩次交互:

  1. 根據(jù)用戶(hù)選擇的點(diǎn)查詢(xún)出緩沖區(qū);
  2. 查詢(xún)緩沖區(qū)內(nèi)的建筑物信息;

這個(gè)例子在 GIS 查詢(xún)中可以說(shuō)是非常簡(jiǎn)單的, 也是很典型的, ESRI 的例子中也給出了完整的源代碼, 這個(gè)例子的核心邏輯代碼是:

_geometryService = new GeometryService(GeoServerUrl); _geometryService.BufferCompleted += GeometryService_BufferCompleted;  _queryTask = new QueryTask(QueryTaskUrl); _queryTask.ExecuteCompleted += QueryTask_ExecuteCompleted;  void MyMap_MouseClick(object sender, Map.MouseEventArgs e) {    // 部分代碼省略, 開(kāi)始緩沖查詢(xún)    _geometryService.BufferAsync(bufferParams);  }  void GeometryService_BufferCompleted(object sender, GraphicsEventArgs args) {    // 部分代碼省略, 獲取緩沖查詢(xún)結(jié)果, 開(kāi)始查詢(xún)緩沖區(qū)內(nèi)的建筑物信息    _queryTask.ExecuteAsync(query); }  void QueryTask_ExecuteCompleted(object sender, QueryEventArgs args) {    // 將查詢(xún)結(jié)果更新到界面上 } 

這只是一個(gè) GIS 開(kāi)發(fā)中很簡(jiǎn)單的一個(gè)查詢(xún), 上面的代碼卻將邏輯分散在三個(gè)函數(shù)中, 在實(shí)際應(yīng)用中, 與服務(wù)端的交互次數(shù)會(huì)更多, 代碼的邏輯會(huì)分散在更多的函數(shù)中, 導(dǎo)致代碼的可讀性以及可維護(hù)性降低。 如果使用 Task 對(duì)象對(duì)這些任務(wù)進(jìn)行封裝, 那么整個(gè)邏輯將會(huì)簡(jiǎn)潔很多, GeometryService 和 QueryTask 提供的是 EPM 異步模式, 相應(yīng)的封裝方法如上所示, 最后, 用 Task 封裝異步操作之后的代碼如下:

void MyMap_MouseClick(object sender, Map.MouseEventArgs e) {    Task.Factory.StartNew(() => {       // 省略部分 UI 代碼, 開(kāi)始緩沖查詢(xún)       var bufferParams = new BufferParameters() { /* 初始化緩沖查詢(xún)參數(shù) */};       var bufferTask = _geometryService.CreateBufferTask()       // 等待緩沖查詢(xún)結(jié)果       bufferTask.Wait();       // 省略更新 UI 的代碼, 開(kāi)始查詢(xún)緩沖區(qū)內(nèi)的建筑物信息       var query = new Query() { /* 初始化查詢(xún)參數(shù) */ };       var queryExecTask = _queryTask.CreateExecTask(query);       queryExecTask.Wait();       // 將查詢(xún)結(jié)果顯示在界面上, 代碼省略    }); } 

從上面的代碼可以看出, 使用 Task 對(duì)象可以把原本分散在三個(gè)函數(shù)中的邏輯集中在一個(gè)函數(shù)中即可完成, 代碼的可讀性、可維護(hù)性比原來(lái)增加了很多。

Task 能完成的任務(wù)遠(yuǎn)不止這些,比如并行計(jì)算、 協(xié)調(diào)多個(gè)并發(fā)任務(wù)等, 有興趣的可以進(jìn)一步閱讀相關(guān)的 MSDN 資料。

相關(guān)軟件

相關(guān)文章

文章評(píng)論

軟件按字母排列: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z