C#多線程之旅(4)——APM初探


源碼地址:https://github.com/Jackson0714/Threads

 

原文地址:C#多線程之旅(4)——APM初探

C#多線程之旅目錄:

C#多線程之旅(1)——介紹和基本概念

C#多線程之旅(2)——創建和開始線程

C#多線程之旅(3)——線程池

C#多線程之旅(4)——APM初探

C#多線程之旅(5)——同步機制介紹

C#多線程之旅(6)——詳解多線程中的鎖

更多文章正在更新中,敬請期待......

 

C#多線程之旅(4)——APM初探

v博客前言

先交代下背景,前面幾張內容主要是介紹多線程的基本知識,這一章是因為正好接觸到了APM(異步編程模型),發現APM真的很強大,其中有部分知識點涉及到了委托的BeginInvoke/EndInvoke,就由衷地想寫下APM相關的知識。

v寫在前面

強大的異步處理模型,不得不被它折服!

v正文開始

一、簡單的串行執行程序

 我們先來看一個簡單的程序:
定義了一個 int Add(int num),傳入循環的次數num,返回循環相加的結果sum。
Step 1.Main方法調用Add方法,循環執行了2次,所以延時了2s,返回結果sum=1,打印 Result:1;
Step 2.Main方法循環執行了3次,延時了3s。
友情提醒:如果覺得不想閱讀多彩的 Console打印代碼,可以選擇查看下面折疊的code區域。 查看簡潔版
 去掉顏色打印的code簡潔版

 讓我們看看這個程序的運行結果:

 我們可以從結果中看到:
  1.執行Add方法,是主線程執行Add方法;
  2.執行Main方法,是主線程執行Main方法;
  3.這中限時操作可以稱為“計算限制的異步操作”;
  4.Add方法中模擬耗時操作(2s)和Main方法中模擬耗時操作(3s)是串行執行的,那么我們有沒有一種方法使這兩種操作並行執行了?(3s中之內搞定這兩個耗時操作)。答案是可以用APM。
 
 下面我們用APM方式來節省2s的時間。

 二、使用委托來實現APM

2.1 預備知識

我們使用泛型委托來實現APM,那么我們需要點預備知識(對委托很熟練的同學們可以跳過預備知識):
  1.什么是委托?
  2.什么是泛型委托?
  3.為什么使用委托來實現APM?
對於這知識點1、2,可以參考我之前寫的博客,在這里就不再說明了, 不懼面試:委托
對於第三個知識點,是因為委托定義了兩個異步方法 BeginInvokeEndInvoke
我們可以先看看泛型委托的定義:
/// <summary>
/// 定義一個泛型委托
/// </summary>
/// <typeparam name="T">輸入參數</typeparam>
/// <typeparam name="TResult">返回值</typeparam>
/// <param name="arg">輸入參數</param>
/// <returns name="TResult">返回值</returns>
private delegate TResult Func<T, TResult>(T arg);

對於這個定義,C#編譯器會將這行代碼編譯成一個類定義,它的邏輯定義如下:

public sealed class Func<T, TResult> : MulticastDelegate
{
	public Func(Object obj, IntPtr method);
	public TResult Invoke(T arg);
	public IAsyncResult BeginInvoke(T arg, AsyncCallback callback, Object obj);
	public TResult EndInvoke(IAsyncResult result);
}

定義一個委托時,會生成一個BeginInvoke和EndInvoke方法的類。

當定義下面的委托時

public delegate void myDelegate(int value);

通過反編譯工具ILSpy查看結果:

BeginInvoke:

  1.第一個參數arg為委托定義相同的參數(可以為兩個參數arg,和委托的簽名相同),可以傳入到委托引用的方法;

  2.倒數第二個參數callback為回調方法,當BeginInvoke方法執行完后,會立即調用回調方法,如果callback=null,則不調用回調方法;

  3.倒數第一個參數object給EndInvoke用的。

  4.返回值為IAsyncResult類型的接口對象(實際上是AsynResult的類型實例)。該接口對象用途

    a.傳遞參數,它包含了對調用了BeginInvoke的委托的引用,這里是Add方法的int類型的輸入參數;

    b.包含了BeginInvoke()的最后一個Object類型的參數

    c.它可以鑒別是哪個方法的哪一次調用,因為通過同一個委托變量可以對同一個方法調用多次。

EndInvoke

  1.第一個參數接收BeginInvoke返回的IAnsyResult;

  2.返回的TResult為委托引用的方法的返回值,這里是Add方法的int類型返回值

2.2 用委托來實現APM的原理

2.3 動手寫個實現了APM的Code

通過上面的流程圖,相信我們對委托來實現APM有了一定的理解,再來讀讀code,相信能更快地理解。注釋僅作參考,有問題可以回復我哦!

讓我們看看結果:

 注意:

  1.必須先將IAsyncResult轉換為AsyncResult,才能獲取到引用的委托,因為它沒有包含在IAsyncResult接口的定義中;

  2.Add方法的調用,AddCallback方法都是線程池線程調用的;

  3.BeginInvoke的object參數可以為任何類型,例子中傳遞的是string類型的參數"I'm here!";

  4.主線程執行的for循環和Add方法中線程是同時進行的,交替打印結果;

  5.當異步的Add方法沒有執行完畢,調用EndInvoke,則會阻塞當前線程池線程,只有異步方法執行完畢后,才會繼續執行的代碼;

  6.Add方法執行完后,會自動調用回調方法AddCallback;

  7.在調用EndInvoke可能拋出異常,所以需要加try/catch/finally,捕獲EndInvoke的可能拋出的異常。

 
v寫在最后

因為只是剛開始接觸APM相關的知識,所以本篇只是寫初探的內容,后面的章節會更多地介紹這方面的內容。希望得到園友們的支持!


作  者: Jackson0714
出  處:http://www.cnblogs.com/jackson0714/
關於作者:專注於微軟平台的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角推薦一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM