從.NET 4.5開始,支持的三種異步編程模式:
•基於事件的異步編程設計模式 (EAP,Event-based Asynchronous Pattern)
•異步編程模型(APM,Asynchronous Programming Model)
•基於任務的編程模型(TAP,Task-based Asynchronous Pattern)
基於任務的異步模式 (TAP) 是基於 System.Threading.Tasks 命名空間的 Task 和 Task<TResult>,用於表示任意異步操作。 TAP 是新開發的建議異步設計模式,之後再討論。
先總結一下舊有的2種模式:EAP、APM。
從以下幾個方面,看這2種異步編程方式的異同:
•命名、參數、返回值
•典型應用
•捕獲異常
•狀態
•取消操作
•進度報告
EAP 命名、參數、返回值 EAP的編程模式的代碼命名有以下特點:
•將有一個或多個名為 “[方法名稱]Async” 的方法。這些方法可能會創建同步版本的鏡像,這些同步版本會在當前線程上執行相同的操作。
•該類還可能有一個 “[方法名稱]Completed” 事件,監聽異步方法的結果。
•它可能會有一個 “[方法名稱]AsyncCancel”(或只是 CancelAsync)方法,用於取消正在進行的異步操作。
參數和返回值都沒有特殊規定,按照業務需求而定
典型應用 以請求一個Url為例
復制代碼 代碼如下:
public class EAP_Typical
{
public static void AsyncRun()
{
Utility.Log("AsyncRun:start");
//測試網址
string url = http://sports.163.com/nba/;
using (WebClient webClient = new WebClient())
{
//獲取完成情況
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri(url));
Utility.Log("AsyncRun:download_start");
}
}
static void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string log = "AsyncRun:download_completed";
//獲取返回結果
log += "|result_size=" + Utility.GetStrLen(e.Result);
Utility.Log(log);
}
}
捕獲異常 異常信息一般在Completed的事件參數中傳遞的。緊接上面的例子,如果需要獲取返回的異常信息,則需要改寫一下DownloadStringComleted的方法。
復制代碼 代碼如下:
static void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string log = "AsyncRun:download_completed";
if (e.Error != null) //可見,在事件的參數傳輸異常信息
{
//出現異常,就記錄異常
log += "|error=" + e.Error.Message;
}
else
{
//沒有出現異常,則記錄結果
log += "|result_size=" + Utility.GetStrLen(e.Result);
}
Utility.Log(log);
}
狀態 EAP本身並沒有維護狀態,如果需要的話,應該設置不同的時間響應不同的狀態改變;
假設剛才的DownloadStringAsync,需要增加多幾個狀態值,可以考慮增加多幾個事件。
如
Event DownloadStringStarted(響應下載剛開始)
Event DownloadStringPending(響應下載阻塞中)
Event DownloadStringCancel(響應下載取消時)
等等。
取消操作 按命名規范,如果操作對應有“[方法名稱]AsyncCancel”(或只是 CancelAsync)方法,則支持取消操作。
取消的狀態捕獲,還是以剛才的下載Url輸出html為例,還是在DownloadStringCompleted 獲取取消與否的狀態。DownloadStringCompletedEventArgs. Cancelled
注意的是,如果用戶執行了CancelAsync後,在DownloadStringCompletedEventArgs.Error就會獲取到對應的異常,此時不要再取DownloadStringCompletedEventArgs.Result。
進度報告 EAP沒有硬性規定說要支持進度報告,但可以很順其自然地通過時間響應進度變化。
以當前例子,WebClient 就提供了DownloadProgressChanged 做進度變化的響應事件。
APM 命名、參數、返回值 APM的編程模式的代碼命名有以下特點:
•使用 IAsyncResult 設計模式的異步操作是通過名為[Begin操作名稱] 和 [End操作名稱] 的兩個方法來實現的,這兩個方法分別開始和結束異步操作 操作名稱。 例如,FileStream 類提供 BeginRead 和 EndRead 方法來從文件異步讀取字節。 這兩個方法實現了 Read 方法的異步版本。
•在調用 [Begin操作名稱] 後,應用程序可以繼續在調用線程上執行指令,同時異步操作在另一個線程上執行。 每次調用 [Begin操作名稱] 時,應用程序還應調用 [End操作名稱] 來獲取操作的結果。
典型應用 以請求一個Url為例
復制代碼 代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
namespace AsyncTest1.APM
{
public class APMTestRun1
{
public static void AsyncRun()
{
Utility.Log("APMAsyncRun:start");
//測試網址
string url = "http://sports.163.com/nba/";
HttpWebRequest webRequest = HttpWebRequest.Create(url) as HttpWebRequest;
webRequest.BeginGetResponse(Callback, webRequest);
Utility.Log("AsyncRun:download_start");
}
private static void Callback(IAsyncResult ar)
{
var source = ar.AsyncState as HttpWebRequest;
var response = source.EndGetResponse(ar);
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
Utility.Log("AsyncRun:result_size=" + Utility.GetStrLen(content));
}
}
}
}
}
委托的異步調用也用的是APM模式,這個方式的強大之處,在於可以使任何方法編程異步調用。
復制代碼 代碼如下:
/// <summary>
/// 一個耗時的方法
/// </summary>
private static void CaluateManyNumber() {
for (int i = 0; i < 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("loop==>"+i.ToString());
}
}
/// <summary>
/// 委托,讓耗時方法可以異步執行
/// </summary>
public static void AsyncDelegate() {
//委托簡單的包裝了一下方法
Action action = CaluateManyNumber;
action.BeginInvoke(DelegateCallback, null);
Console.WriteLine("action begin");
}
/// <summary>
/// 異步回調
/// </summary>
/// <param name="ar"></param>
private static void DelegateCallback(IAsyncResult ar) {
AsyncResult asyncResult = ar as AsyncResult;
var delegateSource = asyncResult.AsyncDelegate as Action;
delegateSource.EndInvoke(ar);
Console.WriteLine("action end");
}
捕獲異常 異常信息要在[End操作名稱]中獲取。
復制代碼 代碼如下:
private static void Callback(IAsyncResult ar)
{
var source = ar.AsyncState as HttpWebRequest;
WebResponse response = null;
try
{
response = source.EndGetResponse(ar);
}
catch (Exception ex) {
Utility.Log("error:" + ex.Message);
response = null;
}
if (response != null)
{
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
Utility.Log("AsyncRun:result_size=" + Utility.GetStrLen(content));
}
}
}
}
狀態和取消操作、進度報告 APM模式本身不支持狀態多樣化和取消操作、進度報告。