深入理解C#中的異步(一)——APM模式EAP模式
目錄- 深入理解C#中的異步(一)——APM模式EAP模式
- 1 使用異步編程的原因
- 2 異步編程模式
- 2.1 APM模式
- 2.1.1 APM模式示例代碼
- 2.1.2 執行結果
- 2.1.3 APM回呼例子
- 2.1.4 執行結果
- 2.2 EAP模式
- 2.2.1 EAP模式編程示例1
- 2.2.2 執行結果
- 2.2.3 封裝一個EAP例子
- 2.2.4 執行結果
- 2.1 APM模式
- 3 代碼倉庫
- 4 下篇
1 使用異步編程的原因
同步編程,服務器在響A服務的資料庫讀取,網頁請求或者檔案請求(這里我們統稱為IO操作),如果延遲很大,此時如果來了B服務的IO請求,可能無法及時回應(阻塞),此時異步編程模式(非阻塞)應運而生,
異步編程模式是為了避免性能瓶頸并增強你的應用程式的總體回應能力,
2 異步編程模式
2.1 APM模式
APM(Asynchronous Programming Model) 是 net 1.0時期就提出的一種異步模式,并且基于IAsyncResult介面實作BeginXXX和EndXXX類似的方法.
2.1.1 APM模式示例代碼
class Program
{
static void Main(string[] args)
{
Console.WriteLine("===== 異步呼叫 AsyncInvokeTest =====");
WebResponseHandler handler = new WebResponseHandler(WebContentLength.GetResult);
//IAsyncResult: 異步操作介面(interface)
//BeginInvoke: 委托(delegate)的一個異步方法的開始
IAsyncResult result = handler.BeginInvoke( null, null);
Console.WriteLine("繼續做別的事情,");
//異步操作回傳
Console.WriteLine(handler.EndInvoke(result));
Console.ReadKey();
}
}
public delegate string WebResponseHandler();
public class WebContentLength
{
public static string GetResult()
{
var client = new WebClient();
var content = client.DownloadString(new Uri("http://cnblogs.com"));
return "網頁字數統計:"+content.Length;
}
}
2.1.2 執行結果

備注:APM又是建立在委托之上的,Net Core中的委托 不支持異步呼叫,也就是 BeginInvoke 和 EndInvoke 方法,即現代異步編程模型中,官方不推薦此模型,此例子使用 .Net FrameWork4.7框架,
2.1.3 APM回呼例子
當異步請求回應完成之后,會自動去呼叫回呼方法,將網頁字數統計結果列印,
class Program
{
static void Main(string[] args)
{
Console.WriteLine("===== 異步回呼 AsyncInvokeTest =====");
WebResponseHandler handler = new WebResponseHandler(WebContentLength.GetResult);
//異步操作介面(注意BeginInvoke方法的不同!)
IAsyncResult result = handler.BeginInvoke( new AsyncCallback(CalllBack), "AsycState:OK");
Console.WriteLine("繼續做別的事情,");
Console.ReadKey();
}
static void CalllBack(IAsyncResult result)
{
WebResponseHandler handler = (WebResponseHandler)((AsyncResult)result).AsyncDelegate;
Console.WriteLine(handler.EndInvoke(result));
Console.WriteLine(result.AsyncState);
}
}
public delegate string WebResponseHandler();
public class WebContentLength
{
public static string GetResult()
{
var client = new WebClient();
var content = client.DownloadString(new Uri("http://cnblogs.com"));
return "網頁字數統計:" + content.Length;
}
}
備注:可以看出此種回呼方式與人的思維邏輯相違背,當在回呼函式中存在二級三級回呼時,代碼可讀性變差,編程會變得比平常要困難一些,
2.1.4 執行結果

2.2 EAP模式
EAP(Event-based Asynchronous Pattern)基于事件的異步模式是 .net 2.0提出的,EAP異步編程算是C#對APM的一種補充,讓異步編程擁有了一系列狀態事件,實作了基于事件的異步模式的類將具有一個或者多個以Async為后綴的方法和對應的Completed事件,并且這些類都支持異步方法的取消、進度報告和報告結果,然而.net中并不是所有的類都支持EAP,
當我們使用EAP模式進行異步編程時,需要滿足以下2個條件:
- 要進行異步的方法其方法名應該以XXXAsync結尾
- 要有一個名為XXXCompleted的事件監聽異步方法的完成
- 可增加一個CancelAsync方法用于取消正在執行的異步方法(可選)
備注:當呼叫基于事件的EAP模式的類的XXXAsync方法時,就開始了一個異步操作,并且基于事件的EAP模式是基于APM模式之上的,EAP 是在 .NET Framework 2.0 版中引入的,在 winform,silverlight或者wpf變成中經常用到,
2.2.1 EAP模式編程示例1
class Program
{
static void Main(string[] args)
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += Wc_DownloadStringCompleted;
wc.DownloadStringAsync(new Uri("http://www.baidu.com"));
Console.WriteLine("執行其他任務,");
Console.ReadKey();
}
private static void Wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
Console.WriteLine("網頁字數統計:" + e.Result.Length);
}
}
2.2.2 執行結果

總結:此示例代碼的編程模式有沒有種似曾相識的感覺,沒錯,winform,wpf等的點擊事件,網路庫的接收方法中采用事件驅動型的異步編程模式,
2.2.3 封裝一個EAP例子
示例代碼如下:
Work類,如下代碼使用了了事件驅動型異步編程模式,并且對APM模式進行了封裝,
/// <summary>
/// EAP是對APM的封裝
/// </summary>
public class Worker
{
public enum WorkerStatus
{
Cancel = 0, Running = 1, Completed = 2
}
public class WorkerEventArgs : EventArgs
{
public WorkerStatus Status { get; set; }
public string Message { get; set; }
}
public Worker()
{
}
public event EventHandler<WorkerEventArgs> OnWorkCompleted;
IAsyncResult asyncResult = null;
Thread thread = null;
public void WorkAsync()
{
Worker _this = this;
Action action = () =>
{
thread = Thread.CurrentThread;
Thread.Sleep(1000);
Console.WriteLine(string.Format("執行緒:{0},Work Over.", Thread.CurrentThread.ManagedThreadId));
};
//result是IAsyncResult物件,此處無用
//當action委托完成呼叫之后,會呼叫如下回呼方法,
asyncResult = action.BeginInvoke((result) =>
{
WorkerEventArgs e = null;
try
{
action.EndInvoke(result);
}
catch (ThreadAbortException ex)
{
e = new WorkerEventArgs() { Status = WorkerStatus.Cancel, Message = "異步操作被取消" };
}
if (null != _this.OnWorkCompleted)
{
_this.OnWorkCompleted.Invoke(this, e);
}
},this);
}
public void CancelAsync()
{
if (null != thread)
thread.Abort();
}
}
winform呼叫例子
異步嗲用WorkAsync,完成之后,事件異步呼叫WorkOver方法,并傳入EventArgs引數,
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Worker worker;
private void btnStart_Click(object sender, EventArgs e)
{
worker = new Worker();
worker.OnWorkCompleted += WorkOver;
worker.WorkAsync();
Console.WriteLine(string.Format("執行緒:{0}", Thread.CurrentThread.ManagedThreadId));
}
private void btnCancel_Click(object sender, EventArgs e)
{
worker.CancelAsync();
}
private void WorkOver(object sender, Worker.WorkerEventArgs e)
{
if (null != e)
{
if (Worker.WorkerStatus.Cancel == e.Status)
{
MessageBox.Show(e.Message);
}
}
else
{
Console.WriteLine(string.Format("執行緒:{0},委托回呼完成.", Thread.CurrentThread.ManagedThreadId));
}
}
}
2.2.4 執行結果
- 執行完成

- 未執行完成提前取消

注意事項(重要):
- APM異步編程時,因異步代碼執行在單獨的執行緒中,異步代碼中出現的例外應該在呼叫EndXXX時捕獲,
- EAP異步編程時,因上述同樣原因,代碼中的例外資訊會被傳遞到Completed事件的EventArgs引數中,
3 代碼倉庫
本文中的代碼
4 下篇
預告:
深入理解C#中的異步(二)——TAP模式(基于Async,Await,Task的異步)
著作權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 著作權協議,轉載請附上原文出處鏈接和本宣告, 本文鏈接:https://www.cnblogs.com/JerryMouseLi/p/14100496.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/231416.html
標籤:架構設計
