日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
異步編程:異步編程模型(APM)

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

創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)營銷推廣、網(wǎng)站重做改版、集安網(wǎng)站定制設計、自適應品牌網(wǎng)站建設、成都h5網(wǎng)站建設、購物商城網(wǎng)站建設、集團公司官網(wǎng)建設、外貿(mào)營銷網(wǎng)站建設、高端網(wǎng)站制作、響應式網(wǎng)頁設計等建站業(yè)務,價格優(yōu)惠性價比高,為集安等各大城市提供網(wǎng)站開發(fā)制作服務。

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

1. 在一些特殊場合下我們可能覺得一種模式更適合;

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

3. 很多遺留的代碼包含了舊的設計模式;

4. 等等…

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

IAsyncResult設計模式----規(guī)范概述

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

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

1) Begin***

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

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

c) 返回IAsyncResult對象。

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

2) End***

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

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

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

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

現(xiàn)在我們清楚了IAsyncResult設計模式的設計規(guī)范,接下來我們再通過IAsyncResult異步編程模式的三個經(jīng)典場合來加深理解。

#p#

一、基于IAsyncResult構造一個異步API

現(xiàn)在來構建一個IAsyncResult的類,并且實現(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.     /// 存儲最后結果值
  48.     /// </summary>
  49.     public int FinnalyResult { get; set; }
  50.     /// <summary>
  51.     /// End方法只應調(diào)用一次,超過一次報錯
  52.     /// </summary>
  53.     public int EndCallCount = 0;
  54.     /// <summary>
  55.     /// ref參數(shù)
  56.     /// </summary>
  57.     public string ResultStr;
  58.  
  59.     /// <summary>
  60.     /// 異步進行耗時計算
  61.     /// </summary>
  62.     /// <param name="obj">CalculateAsyncResult實例本身</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.     }
  106. }

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

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

a) IAsyncResult的AsyncWaitHandle屬性,待異步操作完成時獲得信號。

b) 通過IAsyncResult的IsCompleted屬性進行輪詢。通過輪詢還可實現(xiàn)進度條功能。

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

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

 
 
 
  1. public class Calculate_Test
  2. {
  3.     public static void Test()
  4.     {
  5.         CalculateLib cal = new CalculateLib();
  6.  
  7.         // 基于IAsyncResult構造一個異步API
  8.         IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal);
  9.         // 執(zhí)行異步調(diào)用后,若我們需要控制后續(xù)執(zhí)行代碼在異步操作執(zhí)行完之后執(zhí)行,可通過下面三種方式阻止其他工作:
  10.         // 1、IAsyncResult 的 AsyncWaitHandle 屬性,帶異步操作完成時獲得信號。
  11.         // 2、通過 IAsyncResult 的 IsCompleted 屬性進行輪詢。通過輪詢還可實現(xià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.     /// 
  22.     /// 異步操作完成后做出響應
  23.     /// 
  24.     private static void AfterCallback(ref string resultStr, IAsyncResult ar)
  25.     {
  26.         // 執(zhí)行異步調(diào)用后,若我們不需要阻止后續(xù)代碼的執(zhí)行,那么我們可以把異步執(zhí)行操作后的響應放到回調(diào)中進行。
  27.         CalculateLib cal = ar.AsyncState as CalculateLib;
  28.  
  29.         //int result = cal.EndInvoke(ref resultStr, calculateResult1);
  30.         //if (result > 0) { }
  31.     }
  32. }

#p#

二、使用委托進行異步編程

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

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

示例:

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

 
 
 
  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;
  7. }

然后,通過委托進行同步或異步調(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#

三、多線程操作控件

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

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

1. 非安全方式訪問控件(此方式請永遠不要再使用)

多線程訪問窗口中的控件,可以在窗口的構造函數(shù)中將Form的CheckForIllegalCrossThreadCalls靜態(tài)屬性設置為false。

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

2. 安全方式訪問控件

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

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

1) 使用BackgroundWork后臺輔助線程控件控件(AsyncOperationManager類和AsyncOperation類幫助器方式)。

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

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

因本文主要解說IAsyncResult異步編程模式,所以只詳細分析Invoke 和BeginInvoke跨線程訪問控件方式。

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

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

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

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

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

 
 
 
  1. UnsafeNativeMethods.PostMessage(new HandleRef(this, this.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,用來把一個消息發(fā)送到一個窗口的消息隊列。這個方法是異步的,也就是該方法封送完畢后馬上返回,不會等待委托方法的執(zhí)行結束,調(diào)用者線程將不會被阻塞。(對應同步方法的windows api是:SendMessage())

2) InvokeRequired

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

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

即返回“通過GetWindowThreadProcessId功能函數(shù)得到創(chuàng)建指定窗口線程的標識和創(chuàng)建窗口的進程的標識符與當前線程Id進行比較”的結果。

3) 示例(詳見示例文件)

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

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

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

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


分享標題:異步編程:異步編程模型(APM)
網(wǎng)站鏈接:http://m.5511xx.com/article/ccddigj.html