日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】1.6异步编程:IAsyncResult异步编程模型 (APM)

發(fā)布時間:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】1.6异步编程:IAsyncResult异步编程模型 (APM) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

傳送門:異步編程系列目錄……

?????????大部分開發(fā)人員,在開發(fā)多線程應(yīng)用程序時,都是使用ThreadPool的QueueUserWorkItem方法來發(fā)起一次簡單的異步操作。然而,這個技術(shù)存在許多限制。最大的問題是沒有一個內(nèi)建的機(jī)制讓你知道操作在什么時候完成,也沒有一個機(jī)制在操作完成時獲得一個返回值。為了克服這些限制(并解決其他一些問題),Microsoft引入了三種異步編程模式:

1.?????????.NET1.0異步編程模型?(APM),基于IAsyncResult接口實(shí)現(xiàn)。

2.?????????.NET2.0基于事件的異步編程模式(EAP),基于事件實(shí)現(xiàn)。

3.?????????.NET4.X基于任務(wù)的異步編程模式(TPL->TAP),新型異步編程模式,對于.NET4.0之后的異步構(gòu)造都推薦使用此模式

盡管在新的設(shè)計(jì)上我們推薦都使用“.NET4.0基于任務(wù)的編程模式”,但我還是計(jì)劃整理出舊版的異步編程模型,因?yàn)?#xff1a;

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

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

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

4.?????????等等…

?

?

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

?

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

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

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

1)?????????Begin***

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

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

c)?????????返回IAsyncResult對象。

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

2)?????????End***

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

b)?????????End***?方法的返回值與其同步副本的返回值類型相同。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)中獲得對應(yīng)引用傳參值做相應(yīng)邏輯;

3)?????????總是調(diào)用?End***()?方法,而且只調(diào)用一次

以下理由都是針對“I/O限制”的異步操作提出。然而,對于計(jì)算限制的異步操作,盡管都是用戶代碼,但還是推薦遵守此規(guī)則。

I/O限制的異步操作:比如像帶FileOptions.Asynchronous標(biāo)識的FileStream,其BeginRead()方法向Windows發(fā)送一個I/O請求包(I/O Request Packet,IRP)后方法不會阻塞線程而是立即返回,由Windows將IRP傳送給適當(dāng)?shù)脑O(shè)備驅(qū)動程序,IRP中包含了為BeginRead()方法傳入的回調(diào)函數(shù),待硬件設(shè)備處理好IRP后,會將IRP的委托排隊(duì)到CLR的線程池隊(duì)列中。

必須調(diào)用End***方法,否則會造成資源的泄露。有的開發(fā)人員寫代碼調(diào)用Begin***方法異步執(zhí)行I/O限制后就不需要進(jìn)行任何處理了,所以他們不關(guān)心End***方法的調(diào)用。但是,出于以下兩個原因,End***方法是必須調(diào)用的:

a)?????????在異步操作時,對于I/O限制操作,CLR會分配一些內(nèi)部資源,操作完成時,CLR繼續(xù)保留這些資源直至End***方法被調(diào)用。如果一直不調(diào)用End***,這些資源會直到進(jìn)程終止時才會被回收。(End***方法設(shè)計(jì)中常常包含資源釋放)

b)?????????發(fā)起一個異步操作時,實(shí)際上并不知道該操作最終是成功還是失敗(因?yàn)椴僮饔捎布趫?zhí)行)。要知道這一點(diǎn),只能通過調(diào)用End***方法,檢查它的返回值或者看它是否拋出異常。

另外,需要注意的是I/O限制的異步操作完全不支持取消(因?yàn)椴僮饔捎布?zhí)行),但可以設(shè)置一個標(biāo)識,在完成時丟棄結(jié)果來模擬取消行為。

?

?

?

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

?

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

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

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

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

1.?????????執(zhí)行異步調(diào)用后,若我們需要控制后續(xù)執(zhí)行代碼在異步操作執(zhí)行完之后執(zhí)行,可通過下面三種方式阻止其他工作:(當(dāng)然我們不推薦你阻塞線程或輪詢浪費(fèi)CPU時間)

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

b)?????????通過IAsyncResult的IsCompleted屬性進(jìn)行輪詢。

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 /// <summary> /// APM 阻塞式異步響應(yīng) /// </summary> public?class?Calculate_For_Break { ????public?static?void?Test() ????{ ????????CalculateLib cal = new?CalculateLib(); ? ????????// 基于IAsyncResult構(gòu)造一個異步API?? (回調(diào)參數(shù)和狀態(tài)對象都傳遞null) ????????IAsyncResult calculateResult = cal.BeginCalculate(123, 456, null, null); ????????// 執(zhí)行異步調(diào)用后,若我們需要控制后續(xù)執(zhí)行代碼在異步操作執(zhí)行完之后執(zhí)行,可通過下面三種方式阻止其他工作: ????????// 1、IAsyncResult 的 AsyncWaitHandle 屬性,帶異步操作完成時獲得信號。 ????????// 2、通過 IAsyncResult 的 IsCompleted 屬性進(jìn)行輪詢。通過輪詢還可實(shí)現(xiàn)進(jìn)度條功能。 ????????// 3、調(diào)用異步操作的 End*** 方法。 ????????// *********************************************************** ????????// 1、calculateResult.AsyncWaitHandle.WaitOne(); ????????// 2、while (calculateResult.IsCompleted) { Thread.Sleep(1000); } ????????// 3、 ????????string?resultStr = string.Empty; ????????int?result = cal.EndCalculate(ref?resultStr, calculateResult); ????} }

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /// <summary> /// APM 回調(diào)式異步響應(yīng) /// </summary> public?class?Calculate_For_Callback { ????public?static?void?Test() ????{ ????????CalculateLib cal = new?CalculateLib(); ? ????????// 基于IAsyncResult構(gòu)造一個異步API ????????IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal); ????} ? ????/// <summary> ????/// 異步操作完成后做出響應(yīng) ????/// </summary> ????private?static?void?AfterCallback(ref?string?resultStr, IAsyncResult ar) ????{ ????????// 執(zhí)行異步調(diào)用后,若我們不需要阻止后續(xù)代碼的執(zhí)行,那么我們可以把異步執(zhí)行操作后的響應(yīng)放到回調(diào)中進(jìn)行。 ????????CalculateLib cal = ar.AsyncState as?CalculateLib; ????????cal.EndCalculate(ref?resultStr, ar); ????????// 再根據(jù)resultStr值做邏輯。 ????} }

?

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

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

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

示例:

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

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /// <summary> /// 使用委托進(jìn)行異步調(diào)用 /// </summary> public?class?Calculate_For_Delegate { ????public?static?void?Test() ????{ ????????CalculateLib cal = new?CalculateLib(); ? ????????// 使用委托進(jìn)行同步或異步調(diào)用 ????????AsyncInvokeDel calculateAction = cal.Calculate; ????????string?resultStrAction = string.Empty; ????????// int result1 = calculateAction.Invoke(123, 456); ????????IAsyncResult calculateResult1 = calculateAction.BeginInvoke(123, 456, ref?resultStrAction, null, null); ????????int?result1 = calculateAction.EndInvoke(ref?resultStrAction, calculateResult1); ????} }

?

三、多線程操作控件

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

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

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

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

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

2.?????????安全方式訪問控件

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

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

1)?????????使用BackgroundWork后臺輔助線程控件方式(詳見:基于事件的異步編程模式(EMP))。

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

3)?????????捕獲線程上下文ExecuteContext,并調(diào)用ExeceteContext.Run()靜態(tài)方法在指定的線程上下文中執(zhí)行。(詳見:執(zhí)行上下文

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

5)?????????在WPF應(yīng)用程序中可以通過WPF提供的Dispatcher對象提供的Invoke方法、BeginInvoke方法來完成跨線程工作。

?

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

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

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

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

a)?????????Invoke????????????????(同步調(diào)用)先判斷控件創(chuàng)建線程與當(dāng)前線程是否相同,相同則直接調(diào)用委托方法;否則使用Win32API的PostMessage?異步執(zhí)行,但是 Invoke 內(nèi)部會調(diào)用IAsyncResult.AsyncWaitHandle等待執(zhí)行完成。

b)?????????BeginInvoke?????(異步調(diào)用)使用Win32API的PostMessage?異步執(zhí)行,并且返回 IAsyncResult 對象。

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

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

2)?????????InvokeRequired

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

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

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

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

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

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

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

4)?????????異常信息:"在創(chuàng)建窗口句柄之前,不能在控件上調(diào)用?Invoke?或?BeginInvoke"

a)?????????可能是在窗體還未構(gòu)造完成時,在構(gòu)造函數(shù)中異步去調(diào)用了Invoke?或BeginInvoke;

b)?????????可能是使用輔助線程創(chuàng)建一個窗口并用Application.Run()去創(chuàng)建句柄,在句柄未創(chuàng)建好之前調(diào)用了Invoke?或BeginInvoke。(此時新建的窗口相當(dāng)于開了另一個進(jìn)程,并且為新窗口關(guān)聯(lián)的輔助線程開啟了消息循環(huán)機(jī)制),類似下面代碼:

1 2 3 4 5 new?Thread((ThreadStart)delegate ????{ ????????WaitBeforeLogin = new?Form2(); ????????Application.Run(WaitBeforeLogin); ????}).Start();

解決方案:在調(diào)用Invoke?或?BeginInvoke之前輪詢檢查窗口的IsHandleCreated屬性。

1 2 3 // 獲取一個值,該值指示控件是否有與它關(guān)聯(lián)的句柄。 public?bool?IsHandleCreated { get; } while?(!this.IsHandleCreated) { …… }

?

?

?

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

?

?

?

參考:MSDN

??????????????書籍:《CLR via C#(第三版)》

?


作者:滴答的雨
出處:http://www.cnblogs.com/heyuquan/
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。

???
?

“1. 執(zhí)行異步調(diào)用后,若我們需要控制后續(xù)執(zhí)行代碼在異步操作執(zhí)行完之后執(zhí)行,可通過下面三種方式阻止其他工作:(當(dāng)然我們不推薦你阻塞線程或輪詢浪費(fèi)CPU時間)

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

b) 通過IAsyncResult的IsCompleted屬性進(jìn)行輪詢。

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

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


你說的a、b、c方式恰好都是不推薦的調(diào)用方式,因?yàn)闀枞骄€程,并且還給人“在xxx之后完成必須要通過阻塞線程這種方式”的錯誤印象。恰好推薦的最常用的回調(diào)方式你又沒說清楚,所以我認(rèn)為你沒懂。而且IAsnycResult實(shí)現(xiàn)的還比較繁瑣,沒有錯誤處理,并且會有資源泄露可能。而這幾點(diǎn)正是CLR VIA C#所提及的。

另外,你要不就
1.阻塞線程,在主線程里調(diào)用End(不推薦)
IAsyncResult ar = cal.BeginCalculate(123, 456, null, null);

string resultStr = string.Empty;
int result = cal.EndCalculate(ref resultStr, ar);
//把寫在call back的代碼放在這里

2.要不就采用回調(diào)的方式(推薦)。

而不是像你的代碼里這種既有回調(diào),又阻塞線程的混合的使用方式,看起來就像沒太懂的人寫的代碼。
IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal);
string resultStr = string.Empty;
int result = cal.EndCalculate(ref resultStr, calculateResult);


關(guān)于APM我的看法可以看我的博客,關(guān)于IAsyncResult那一節(jié)的表述。

?
???
支持(0)?反對(0) ?? #18樓?[樓主]?2013-04-28 10:12?|?滴答的雨
???
?

@?浪雪
這個問題我有這樣說:我將展示三種阻塞式響應(yīng)異步調(diào)用和一種無阻塞式委托響應(yīng)異步調(diào)用。并且也在括號中說了阻塞方式不推薦,接著立馬就說到回調(diào)方式的不阻塞形式了啊???(你提出的問題還是很好的,我博文中表達(dá)不清,現(xiàn)在我將推薦和不推薦用粗體強(qiáng)調(diào)出來)

對于即有回調(diào)又有阻塞這種代碼可能給予了誤解,實(shí)際上我是想在在這個示例中展現(xiàn)這些關(guān)鍵代碼(并且模式?jīng)_突的代碼我也已經(jīng)注釋掉了,只是為了展現(xiàn)),不想再另外貼出一個示例。。。

感謝你的提示,我將此處表達(dá)更改為如下:

?
???
?

@?滴答的雨
IAsyncResult本身就帶這幾種模式給人選擇的靈活性,并不意味著需要一起用。一起用反而讓人看起來不但困擾,而且反而是錯誤用法。

這就是為什一定需要分開寫,就算是像我一樣偷懶也可以不寫,但是就是不能混在一起寫。

簡單來說,你的代碼中,
1.看起來展示的是直接調(diào)用的end的這種阻塞的方式。
2.同時定義了call back方法給人以困擾,讓人以為這又是個回調(diào)方法。
3.因?yàn)橹苯诱{(diào)用end的方式根本不需要通過asyncState來輔助傳遞參數(shù),避免轉(zhuǎn)型的開銷和可能產(chǎn)生的錯誤。

結(jié)果是你既沒正確的展示End的用法,又把最常用的回調(diào)方法給注釋掉了。
就算你多加一個例子,也不過多三五行的代碼而已。所以就覺得你沒懂。

CalculateLib cal = new CalculateLib();

展示End
IAsyncResult ar = cal.BeginCalculate(123, 456, null, null);
string resultStr = string.Empty;
int result = cal.EndCalculate(ref resultStr, ar);



展示call back
IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal);

private static void AfterCallback(ref string resultStr, IAsyncResult ar)
{
CalculateLib cal = ar.AsyncState as CalculateLib;

int result = cal.EndCalculate(ref resultStr, calculateResult1);
if (result > 0) { }
}

總結(jié)

以上是生活随笔為你收集整理的【转】1.6异步编程:IAsyncResult异步编程模型 (APM)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。