自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

異步編程:異步編程模型 (APM)

開(kāi)發(fā) 后端
隨著技術(shù)的發(fā)展,又在“.NET1.0異步編程模型 (APM)”之后推出了“.NET2.0基于事件的編程模式”及“.NET4.X基于任務(wù)的編程模式”兩種異步編程模式。

異步編程模型 (APM)是.NET1.0的時(shí)候就已經(jīng)推出的古老異步編程模式,此模式基于IAsyncResult接口實(shí)現(xiàn)。

隨著技術(shù)的發(fā)展,又在“.NET1.0異步編程模型 (APM)”之后推出了“.NET2.0基于事件的編程模式”及“.NET4.X基于任務(wù)的編程模式”兩種異步編程模式。盡管在新的設(shè)計(jì)上我們推薦都使用“.NET4.0基于任務(wù)的編程模式”,但我還是計(jì)劃整理出舊版的異步編程模型,因?yàn)椋?/p>

1. 在一些特殊場(chǎng)合下我們可能覺(jué)得一種模式更適合;

2. 可以更充分認(rèn)識(shí)三種模式之間的優(yōu)劣,便于選擇;

3. 很多遺留的代碼包含了舊的設(shè)計(jì)模式;

4. 等等…

 

示例下載:異步編程:IAsyncResult異步編程模型.rar

 

IAsyncResult設(shè)計(jì)模式----規(guī)范概述

使用IAsyncResult設(shè)計(jì)模式的異步操作是通過(guò)名為 Begin*** 和 End*** 的兩個(gè)方法來(lái)實(shí)現(xiàn)的,這兩個(gè)方法分別指代開(kāi)始和結(jié)束異步操作。例如,F(xiàn)ileStream類(lèi)提供BeginRead和EndRead方法來(lái)從文件異步讀取字節(jié)。這兩個(gè)方法實(shí)現(xiàn)了 Read 方法的異步版本。

在調(diào)用 Begin*** 后,應(yīng)用程序可以繼續(xù)在調(diào)用線(xiàn)程上執(zhí)行指令,同時(shí)異步操作在另一個(gè)線(xiàn)程上執(zhí)行。(如果有返回值還應(yīng)調(diào)用 End*** 來(lái)獲取操作的結(jié)果)。

1) Begin***

a) Begin*** 方法帶有該方法的同步版本簽名中聲明的任何參數(shù)。

b) Begin*** 方法簽名中不包含任何輸出參數(shù)。方法簽名最后兩個(gè)參數(shù)的規(guī)范是:第一個(gè)參數(shù)定義一個(gè)AsyncCallback委托,此委托引用在異步操作完成時(shí)調(diào)用的方法。第二個(gè)參數(shù)是一個(gè)用戶(hù)定義的對(duì)象。此對(duì)象可用來(lái)向異步操作完成時(shí)為AsyncCallback委托方法傳遞應(yīng)用程序特定的狀態(tài)信息(eg:可通過(guò)此對(duì)象在委托中訪(fǎng)問(wèn)End*** 方法)。另外,這兩個(gè)參數(shù)都可以傳遞null。

c) 返回IAsyncResult對(duì)象。

  1. // 表示異步操作的狀態(tài)。  
  2. [ComVisible(true)]  
  3. public interface IAsyncResult  
  4. {  
  5.     // 獲取用戶(hù)定義的對(duì)象,它限定或包含關(guān)于異步操作的信息。  
  6.     object AsyncState { get; }  
  7.     // 獲取用于等待異步操作完成的System.Threading.WaitHandle,待異步操作完成時(shí)獲得信號(hào)。  
  8.     WaitHandle AsyncWaitHandle { get; }  
  9.     // 獲取一個(gè)值,該值指示異步操作是否同步完成。  
  10.     bool CompletedSynchronously { get; }  
  11.     // 獲取一個(gè)值,該值指示異步操作是否已完成。  
  12.     bool IsCompleted { get; }  
  13. }  
  14.    
  15. // 常用委托聲明(我后面示例是使用了自定義的帶ref參數(shù)的委托)  
  16. public delegate void AsyncCallback(IAsyncResult ar) 

2) End***

a) End*** 方法可結(jié)束異步操作,如果調(diào)用 End*** 時(shí),IAsyncResult對(duì)象表示的異步操作還未完成,則 End*** 將在異步操作完成之前阻塞調(diào)用線(xiàn)程。

b) End*** 方法的返回值與其同步副本的返回值類(lèi)型相同。End*** 方法帶有該方法同步版本的簽名中聲明的所有out 和 ref 參數(shù)以及由BeginInvoke返回的IAsyncResult,規(guī)范上 IAsyncResult 參數(shù)放最后。

i. 要想獲得返回結(jié)果,必須調(diào)用的方法;

ii. 若帶有out 和 ref 參數(shù),實(shí)現(xiàn)上委托也要帶有out 和 ref 參數(shù),以便在回調(diào)中獲得對(duì)應(yīng)引用傳參值做相應(yīng)邏輯;

 

現(xiàn)在我們清楚了IAsyncResult設(shè)計(jì)模式的設(shè)計(jì)規(guī)范,接下來(lái)我們?cè)偻ㄟ^(guò)IAsyncResult異步編程模式的三個(gè)經(jīng)典場(chǎng)合來(lái)加深理解。

#p#

 

一、基于IAsyncResult構(gòu)造一個(gè)異步API

現(xiàn)在來(lái)構(gòu)建一個(gè)IAsyncResult的類(lèi),并且實(shí)現(xiàn)異步調(diào)用。

  1. // 帶ref參數(shù)的自定義委托  
  2. public delegate void RefAsyncCallback(ref string resultStr, IAsyncResult ar);  
  3.    
  4. public class CalculateAsyncResult : IAsyncResult  
  5. {  
  6.     private int _calcNum1;  
  7.     private int _calcNum2;  
  8.     private RefAsyncCallback _userCallback;  
  9.    
  10.     public CalculateAsyncResult(int num1, int num2, RefAsyncCallback userCallback, object asyncState)  
  11.     {  
  12.         this._calcNum1 = num1;  
  13.         this._calcNum2 = num2;  
  14.         this._userCallback = userCallback;  
  15.         this._asyncState = asyncState;  
  16.         // 異步執(zhí)行操作  
  17.         ThreadPool.QueueUserWorkItem((obj) => { AsyncCalculate(obj); }, this);  
  18.     }  
  19.    
  20.     #region IAsyncResult接口  
  21.     private object _asyncState;  
  22.     public object AsyncState { get { return _asyncState; } }  
  23.    
  24.     private ManualResetEvent _asyncWaitHandle;  
  25.     public WaitHandle AsyncWaitHandle  
  26.     {  
  27.         get  
  28.         {  
  29.             if (this._asyncWaitHandle == null)  
  30.             {  
  31.                 ManualResetEvent event2 = new ManualResetEvent(false);  
  32.                 Interlocked.CompareExchange<ManualResetEvent>(ref this._asyncWaitHandle, event2, null);  
  33.             }  
  34.             return _asyncWaitHandle;  
  35.         }  
  36.     }  
  37.    
  38.     private bool _completedSynchronously;  
  39.     public bool CompletedSynchronously { get { return _completedSynchronously; } }  
  40.    
  41.     private bool _isCompleted;  
  42.     public bool IsCompleted { get { return _isCompleted; } }  
  43.     #endregion  
  44.    
  45.     /// <summary>  
  46.     ///  
  47.     /// 存儲(chǔ)最后結(jié)果值  
  48.     /// </summary>  
  49.     public int FinnalyResult { get; set; }  
  50.     /// <summary>  
  51.     /// End方法只應(yīng)調(diào)用一次,超過(guò)一次報(bào)錯(cuò)  
  52.     /// </summary>  
  53.     public int EndCallCount = 0;  
  54.     /// <summary>  
  55.     /// ref參數(shù)  
  56.     /// </summary>  
  57.     public string ResultStr;  
  58.    
  59.     /// <summary>  
  60.     /// 異步進(jìn)行耗時(shí)計(jì)算  
  61.     /// </summary>  
  62.     /// <param name="obj">CalculateAsyncResult實(shí)例本身</param>  
  63.     private static void AsyncCalculate(object obj)  
  64.     {  
  65.         CalculateAsyncResult asyncResult = obj as CalculateAsyncResult;  
  66.         Thread.SpinWait(1000);  
  67.         asyncResult.FinnalyResult = asyncResult._calcNum1 * asyncResult._calcNum2;  
  68.         asyncResult.ResultStr = asyncResult.FinnalyResult.ToString();  
  69.    
  70.         // 是否同步完成  
  71.         asyncResult._completedSynchronously = false;  
  72.         asyncResult._isCompleted = true;  
  73.         ((ManualResetEvent)asyncResult.AsyncWaitHandle).Set();  
  74.         if (asyncResult._userCallback != null)  
  75.             asyncResult._userCallback(ref asyncResult.ResultStr, asyncResult);  
  76.     }  
  77. }  
  78.    
  79. public class CalculateLib  
  80. {  
  81.     public IAsyncResult BeginCalculate(int num1, int num2, RefAsyncCallback userCallback, object asyncState)  
  82.     {  
  83.         CalculateAsyncResult result = new CalculateAsyncResult(num1, num2, userCallback, asyncState);  
  84.         return result;  
  85.     }  
  86.    
  87.     public int EndCalculate(ref string resultStr, IAsyncResult ar)  
  88.     {  
  89.         CalculateAsyncResult result = ar as CalculateAsyncResult;  
  90.         if (Interlocked.CompareExchange(ref result.EndCallCount, 1, 0) == 1)  
  91.         {  
  92.             throw new Exception("End方法只能調(diào)用一次。");  
  93.         }  
  94.         result.AsyncWaitHandle.WaitOne();  
  95.    
  96.         resultStr = result.ResultStr;  
  97.    
  98.         return result.FinnalyResult;  
  99.     }  
  100.    
  101.     public int Calculate(int num1, int num2, ref string resultStr)  
  102.     {  
  103.         resultStr = (num1 * num2).ToString();  
  104.         return num1 * num2;  
  105.     }  

使用上面通過(guò)IAsyncResult設(shè)計(jì)模式實(shí)現(xiàn)的帶ref引用參數(shù)的異步操作,我將展示三種阻塞式響應(yīng)異步調(diào)用和一種無(wú)阻塞式委托響應(yīng)異步調(diào)用。即:

1. 執(zhí)行異步調(diào)用后,若我們需要控制后續(xù)執(zhí)行代碼在異步操作執(zhí)行完之后執(zhí)行,可通過(guò)下面三種方式阻止其他工作:

a) IAsyncResult的AsyncWaitHandle屬性,待異步操作完成時(shí)獲得信號(hào)。

b) 通過(guò)IAsyncResult的IsCompleted屬性進(jìn)行輪詢(xún)。通過(guò)輪詢(xún)還可實(shí)現(xiàn)進(jìn)度條功能。

c) 調(diào)用異步操作的 End*** 方法。

2. 執(zhí)行異步調(diào)用后,若我們不需要阻止后續(xù)代碼的執(zhí)行,那么我們可以把異步執(zhí)行操作后的響應(yīng)放到回調(diào)中進(jìn)行。

  1. public class Calculate_Test  
  2. {  
  3.     public static void Test()  
  4.     {  
  5.         CalculateLib cal = new CalculateLib();  
  6.    
  7.         // 基于IAsyncResult構(gòu)造一個(gè)異步API  
  8.         IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal);  
  9.         // 執(zhí)行異步調(diào)用后,若我們需要控制后續(xù)執(zhí)行代碼在異步操作執(zhí)行完之后執(zhí)行,可通過(guò)下面三種方式阻止其他工作:  
  10.         // 1、IAsyncResult 的 AsyncWaitHandle 屬性,帶異步操作完成時(shí)獲得信號(hào)。  
  11.         // 2、通過(guò) IAsyncResult 的 IsCompleted 屬性進(jìn)行輪詢(xún)。通過(guò)輪詢(xún)還可實(shí)現(xiàn)進(jìn)度條功能。  
  12.         // 3、調(diào)用異步操作的 End*** 方法。  
  13.         // ***********************************************************  
  14.         // 1、calculateResult.AsyncWaitHandle.WaitOne();  
  15.         // 2、while (calculateResult.IsCompleted) { Thread.Sleep(1000); }  
  16.         // 3、  
  17.         string resultStr = string.Empty;  
  18.         int result = cal.EndCalculate(ref resultStr, calculateResult);  
  19.     }  
  20.    
  21.     /// <summary>  
  22.     /// 異步操作完成后做出響應(yīng)  
  23.     /// </summary>  
  24.     private static void AfterCallback(ref string resultStr, IAsyncResult ar)  
  25.     {  
  26.         // 執(zhí)行異步調(diào)用后,若我們不需要阻止后續(xù)代碼的執(zhí)行,那么我們可以把異步執(zhí)行操作后的響應(yīng)放到回調(diào)中進(jìn)行。  
  27.         CalculateLib cal = ar.AsyncState as CalculateLib;  
  28.    
  29.         //int result = cal.EndInvoke(ref resultStr, calculateResult1);  
  30.         //if (result > 0) { }  
  31.     }  

#p#

二、使用委托進(jìn)行異步編程

對(duì)于委托,編譯器會(huì)為我們生成同步調(diào)用方法“invoke”以及異步調(diào)用方法“BeginInvoke”和“EndInvoke”。對(duì)于異步調(diào)用方式,公共語(yǔ)言運(yùn)行庫(kù) (CLR) 將對(duì)請(qǐng)求進(jìn)行排隊(duì)并立即返回到調(diào)用方,由線(xiàn)程池的線(xiàn)程調(diào)度目標(biāo)方法并與提交請(qǐng)求的原始線(xiàn)程并行運(yùn)行。

異步委托是快速為方法構(gòu)建異步調(diào)用的方式,它基于IAsyncResult設(shè)計(jì)模式實(shí)現(xiàn)的異步調(diào)用,即,通過(guò)BeginInvoke返回IAsyncResult對(duì)象;通過(guò)EndInvoke獲取結(jié)果值。

示例:

上節(jié)的CalculateLib類(lèi)中的同步方法以及所要實(shí)用到的委托如下:

  1. // 帶ref參數(shù)的自定義委托  
  2. public delegate int AsyncInvokeDel(int num1, int num2, ref string resultStr);  
  3. public int Calculate(int num1, int num2, ref string resultStr)  
  4. {  
  5.     resultStr = (num1 * num2).ToString();  
  6.     return num1 * num2;  

然后,通過(guò)委托進(jìn)行同步或異步調(diào)用:

  1. AsyncInvokeDel calculateAction = cal.Calculate;  
  2. string resultStrAction = string.Empty;  
  3. // int result1 = calculateAction.Invoke(123, 456, ref resultStrAction);  
  4. IAsyncResult calculateResult1 = calculateAction.BeginInvoke(123, 456, ref resultStrAction, null, null);  
  5. int result1 = calculateAction.EndInvoke(ref resultStrAction, calculateResult1); 

#p#

三、多線(xiàn)程操作控件

訪(fǎng)問(wèn) Windows 窗體控件本質(zhì)上不是線(xiàn)程安全的。如果有兩個(gè)或多個(gè)線(xiàn)程操作某一控件的狀態(tài),則可能會(huì)迫使該控件進(jìn)入一種不一致的狀態(tài)。還可能出現(xiàn)其他與線(xiàn)程相關(guān)的 bug,包括爭(zhēng)用情況和死鎖。確保以線(xiàn)程安全方式訪(fǎng)問(wèn)控件非常重要。

不過(guò),在有些情況下,您可能需要多線(xiàn)程調(diào)用控件的方法。.NET Framework 提供了從任何線(xiàn)程操作控件的方式:

1. 非安全方式訪(fǎng)問(wèn)控件(此方式請(qǐng)永遠(yuǎn)不要再使用)

多線(xiàn)程訪(fǎng)問(wèn)窗口中的控件,可以在窗口的構(gòu)造函數(shù)中將Form的CheckForIllegalCrossThreadCalls靜態(tài)屬性設(shè)置為false。

  1. // 獲取或設(shè)置一個(gè)值,該值指示是否捕獲對(duì)錯(cuò)誤線(xiàn)程的調(diào)用,  
  2. // 這些調(diào)用在調(diào)試應(yīng)用程序時(shí)訪(fǎng)問(wèn)控件的System.Windows.Forms.Control.Handle屬性。  
  3. // 如果捕獲了對(duì)錯(cuò)誤線(xiàn)程的調(diào)用,則為 true;否則為 false。  
  4. public static bool CheckForIllegalCrossThreadCalls { get; set; } 

2. 安全方式訪(fǎng)問(wèn)控件

原理:從一個(gè)線(xiàn)程封送調(diào)用并跨線(xiàn)程邊界將其發(fā)送到另一個(gè)線(xiàn)程,并將調(diào)用插入到創(chuàng)建控件線(xiàn)程的消息隊(duì)列中,當(dāng)控件創(chuàng)建線(xiàn)程處理這個(gè)消息時(shí),就會(huì)在自己的上下文中執(zhí)行傳入的方法。(此過(guò)程只有調(diào)用線(xiàn)程和創(chuàng)建控件線(xiàn)程,并沒(méi)有創(chuàng)建新線(xiàn)程)

注意:從一個(gè)線(xiàn)程封送調(diào)用并跨線(xiàn)程邊界將其發(fā)送到另一個(gè)線(xiàn)程會(huì)耗費(fèi)大量的系統(tǒng)資源,所以應(yīng)避免重復(fù)調(diào)用其他線(xiàn)程上的控件。

1) 使用BackgroundWork后臺(tái)輔助線(xiàn)程控件控件(AsyncOperationManager類(lèi)和AsyncOperation類(lèi)幫助器方式)。

2) 結(jié)合TaskScheduler.FromCurrentSynchronizationContext() 和Task 實(shí)現(xiàn)。

3) 使用Control類(lèi)上提供的Invoke 和BeginInvoke方法。

 

因本文主要解說(shuō)IAsyncResult異步編程模式,所以只詳細(xì)分析Invoke 和BeginInvoke跨線(xiàn)程訪(fǎng)問(wèn)控件方式。

Control類(lèi)實(shí)現(xiàn)了ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法來(lái)支持其它線(xiàn)程更新GUI界面控件的機(jī)制。

  1. public interface ISynchronizeInvoke  
  2. {  
  3.     // 獲取一個(gè)值,該值指示調(diào)用線(xiàn)程是否與控件的創(chuàng)建線(xiàn)程相同。  
  4.     bool InvokeRequired { get; }  
  5.     // 在控件創(chuàng)建的線(xiàn)程上異步執(zhí)行指定委托。  
  6.     AsyncResult BeginInvoke(Delegate method, params object[] args);  
  7.     object EndInvoke(IAsyncResult asyncResult);  
  8.     // 在控件創(chuàng)建的線(xiàn)程上同步執(zhí)行指定委托。  
  9.     object Invoke(Delegate method, params object[] args);  

1) Control類(lèi)的 Invoke,BeginInvoke 內(nèi)部實(shí)現(xiàn)如下:

a) Invoke (同步調(diào)用)先判斷控件創(chuàng)建線(xiàn)程與當(dāng)前線(xiàn)程是否相同,相同則直接調(diào)用委托方法;否則使用Win32API的PostMessage 異步執(zhí)行。

b) BeginInvoke (異步調(diào)用)使用Win32API的PostMessage 異步執(zhí)行.

  1. UnsafeNativeMethods.PostMessage(new HandleRef(thisthis.Handle)  
  2.                   , threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);  
  3. [DllImport("user32.dll", CharSet=CharSet.Auto)]  
  4. public static extern bool PostMessage(HandleRefhwnd, intmsg, IntPtrwparam, IntPtrlparam); 

PostMessage 是windows api,用來(lái)把一個(gè)消息發(fā)送到一個(gè)窗口的消息隊(duì)列。這個(gè)方法是異步的,也就是該方法封送完畢后馬上返回,不會(huì)等待委托方法的執(zhí)行結(jié)束,調(diào)用者線(xiàn)程將不會(huì)被阻塞。(對(duì)應(yīng)同步方法的windows api是:SendMessage())

2) InvokeRequired

獲取一個(gè)值,該值指示調(diào)用線(xiàn)程是否與控件的創(chuàng)建線(xiàn)程相同。內(nèi)部關(guān)鍵如下:

  1. Int windowThreadProcessId = SafeNativeMethods.GetWindowThreadProcessId(ref2, out num);  
  2. Int currentThreadId = SafeNativeMethods.GetCurrentThreadId();  
  3. return (windowThreadProcessId != currentThreadId); 

即返回“通過(guò)GetWindowThreadProcessId功能函數(shù)得到創(chuàng)建指定窗口線(xiàn)程的標(biāo)識(shí)和創(chuàng)建窗口的進(jìn)程的標(biāo)識(shí)符與當(dāng)前線(xiàn)程Id進(jìn)行比較”的結(jié)果。

3) 示例(詳見(jiàn)示例文件)

在使用的時(shí)候,我們使用 this.InvokeRequired 屬性來(lái)判斷是使用Invoke或BeginInvoke 還是直接調(diào)用方法。

  1. private void InvokeControl(object mainThreadId)  
  2. {  
  3.     if (this.InvokeRequired)  
  4.     {  
  5.         this.Invoke(new Action<String>(ChangeText), "InvokeRequired = true.改變控件Text值");  
  6.         //this.textBox1.Invoke(new Action<int>(InvokeCount), (int)mainThreadId);  
  7.     }  
  8.     else 
  9.     {  
  10.         ChangeText("在創(chuàng)建控件的線(xiàn)程上,改變控件Text值");  
  11.     }  
  12. }  
  13.    
  14. private void ChangeText(String str)  
  15. {  
  16.     this.textBox1.Text += str;  

注意,在InvokeControl方法中使用 this.Invoke(Delegate del) 和使用 this.textBox1.Invoke(Delegate del) 效果是一樣的。因?yàn)樵趫?zhí)行Invoke或BeginInvoke時(shí),內(nèi)部首先調(diào)用 FindMarshalingControl() 進(jìn)行一個(gè)循環(huán)向上回溯,從當(dāng)前控件開(kāi)始回溯父控件,直到找到最頂級(jí)的父控件,用它作為封送對(duì)象。也就是說(shuō) this.textBox1.Invoke(Delegate del) 會(huì)追溯到和 this.Invoke(Delegate del) 一樣的起點(diǎn)。(子控件的創(chuàng)建線(xiàn)程一定是創(chuàng)建父控件的線(xiàn)程,所以這種追溯不會(huì)導(dǎo)致將調(diào)用封送到錯(cuò)誤的目的線(xiàn)程)

 

 

 

本節(jié)到此結(jié)束,本節(jié)主要講了異步編程模式之一“異步編程模型(APM)”,是基于IAsyncResult設(shè)計(jì)模式實(shí)現(xiàn)的異步編程方式,并且構(gòu)建了一個(gè)繼承自IAsyncResult接口的示例,及展示了這種模式在委托及跨線(xiàn)程訪(fǎng)問(wèn)控件上的經(jīng)典應(yīng)用。下一節(jié)中,我將為大家介紹基于事件的編程模型……

原文鏈接:http://www.cnblogs.com/heyuquan/archive/2013/03/22/2976420.html

責(zé)任編輯:林師授 來(lái)源: 博客園
相關(guān)推薦

2014-07-15 10:08:42

異步編程In .NET

2013-04-01 15:25:41

異步編程異步EMP

2020-10-15 13:29:57

javascript

2011-02-22 08:49:16

.NET同步異步

2011-02-22 09:09:21

.NETAsync CTP異步

2014-05-23 10:12:20

Javascript異步編程

2015-04-22 10:50:18

JavascriptJavascript異

2016-09-07 20:43:36

Javascript異步編程

2017-07-13 12:12:19

前端JavaScript異步編程

2023-06-13 13:39:00

多線(xiàn)程異步編程

2022-01-02 09:29:37

模型洋蔥Koa

2013-08-20 15:54:14

異步編程windows編程

2021-06-06 16:56:49

異步編程Completable

2021-06-02 09:01:19

JavaScript 前端異步編程

2011-11-11 15:47:22

JavaScript

2016-12-30 13:43:35

異步編程RxJava

2022-07-08 14:14:04

并發(fā)編程異步編程

2021-03-22 08:45:30

異步編程Java

2017-05-05 08:44:24

PythonAsyncio異步編程

2024-04-24 10:57:54

Golang編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)