使用 Task 簡化非同步編程

来源:http://www.cnblogs.com/ldyblogs/archive/2017/11/10/task.html
-Advertisement-
Play Games

.Net 傳統非同步編程概述 .NET Framework 提供以下兩種執行 I/O 綁定和計算綁定非同步操作的標準模式: 非同步編程模型 (APM),在該模型中非同步操作由一對 Begin/End 方法(如 FileStream.BeginRead 和 Stream.EndRead)表示。 基於事件的非同步 ...


.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 方法為例:

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 事件為例:

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 參數進行回調, 以下麵的方法為例:

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

封裝方法與封裝 EPM 非同步模式類似:

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 的例子中也給出了完整的源代碼, 這個例子的核心邏輯代碼是: 

_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 封裝非同步操作之後的代碼如下:

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 資料。 

以上動圖由“圖鬥羅”提供


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ///沙盒路徑 + (NSString *)pathDocument{ NSFileManager *fileManager = [[NSFileManager alloc]init]; NSString *pathDocuments = [NSSearchPathForDirectoriesInD... ...
  • 作為開發者,想不想知道自己寫了多少行代碼嗎,打開終端,進入項目文件夾,然後進入想統計的某個文件夾,也可以直接在當前項目文件夾,然後終端輸入下麵的代碼就可以了 上面統計了.m、.mm、.cpp、.h、.rss尾碼的所有文件,我是這麼認為的^_^ ...
  • 常用到的屬性字元串 直接賦給label就可以了 ...
  • 前端: js: function AjaxKouBeiShopEdit() { var options = { dataType: "json", success: function (data) { alert(data); }, error: function (XMLHttpRequest, ...
  • BlockingCollection集合是一個擁有阻塞功能的集合,它就是完成了經典生產者消費者的演算法功能。所以BlockingCollection 很適合構造流水線模式的併發方案 BlockingCollection集合是一個擁有阻塞功能的集合,它就是完成了經典生產者消費者的演算法功能。一般情況下,我 ...
  • 原文出處 [Trailmax Tech Max Vasilyev: ASP.Net MVC development in Aberdeen, Scotland ](http://tech.trailmax.info/2014/08/aspnet identity cookie format/) "中 ...
  • 一、自定義Base16編碼原理 Base16編碼跟Base64編碼原理上有點不同,當然前面轉換是一樣的,都是是將輸入的字元串根據預設編碼轉換成一個位元組序列,而這個位元組序列裡面其實就是存的ASCII碼,其次,將每個ASCII碼轉換成8位二進位,每個八位二進位拆分成4位一組的二進位,然後將每4位一組的二 ...
  • 一個新的項目需要用到系統置頂的功能,本來是件很簡單的事情,但是越做發現並不那麼簡單,所以記錄下來: 系統環境:win7旗艦版本64位; IDE :VS2013; WPF+C#開發。 一、開始用Topmost屬性設置為true。 運行木有問題,很簡單喲。呵呵,隨後運行打開osk.exe;卵,被按下去了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...