在上一個專題中為大家介紹了.NET 1.0中提出來的異步編程模式——APM,雖然APM為我們實現異步編程提供了一定的支持,同時它也存在著一些明顯的問題——不支持對異步操作的取消和沒有提供對進度報告的功能,對於有界面的應用程序來說,進度報告和取消操作的支持也是必不可少的,既然存在這樣的問題,微軟當然也應該提供給我們解決問題的方案了,所以微軟在.NET 2.0的時候就為我們提供了一個新的異步編程模型,也就是我這個專題中介紹的基於事件的異步編程模型——EAP。下面就為大家全面介紹了這個異步編程模型。
對於一些朋友可能在平時的工作和學習中已經接觸到了基於事件的異步編程模式的,只是大家可能不知道它使用了異步編程模式罷了(首先我就是這樣的,之前很早就用過BackgroundWorker做過一個小程序,但是不知道該組件是基於事件的異步編程模式),也可能一些朋友之前沒有接觸過基於事件的異步編程模式的,現在就為大家具體介紹下這個在.NET 2.0中提出來的新的異步編程模式。
Async為後綴的方法和對應的Completed事件,並且這些類都支持異步方法的取消、進度報告和報告結果。
當我們調用實現基於事件的異步模式的類的 XxxAsync方法時,即代表開始了一個異步操作,該方法調用完之後會使一個線程池線程去執行耗時的操作,所以當UI線程調用該方法時,當然也就不會堵塞UI線程了。並且基於事件的異步模式是建立了APM的基礎之上的(這也是我在上一專題中詳解介紹APM的原因),而APM又是建立了在委托之上的(對於這點可以參考該系列的APM專題)。然而這點並不是憑空想象的,下面就BackgroundWorker類來給大家詳解解釋EAP是建立在APM的基礎上的。
在深入講解BackgroundWorker類之前,讓我們先看看BackgroundWorker類具有的成員和對應的介紹的(這裡只列出一些在異步編程中經常使用的屬性和方法,具體關於該類成員可以查看MSDN——BackgroundWorker):
官方這樣解釋我就要信?)
)
官方是這麼解釋的,你想知道為什麼調用ReportProgress方法就會觸發ProgressChanged事件嗎?)
在上表中首先提出了我的疑問,官方解釋當我們調用RunWorkerAsync方法時就會觸發DoWork事件,調用ReportProgress方法就會觸發ProgressChanged事件,這裡我就想探究下為什麼會這樣的,為了探究BackgroundWorker類背後的故事,這裡當然就少不了使用反射工具Reflector來查看它的源碼了,現在就進入我們的分析過程。
首先就來分析首先我們看看RunWorkerAsync方法的源碼是如何的:
.RunWorkerAsync( RunWorkerAsync( ( InvalidOperationException(SR.GetString( .isRunning = .cancellationPending = .asyncOperation = AsyncOperationManager.CreateOperation( // 。 .threadStart.BeginInvoke(argument, ,
從上面的代碼中可以證明本專題開始說的 “EAP是基於APM”並不是我憑空想象出來的,而是實現EAP的類確實也是這麼做的,雖然這個假設已經得到證明了,然而從上面的代碼還是不知道解釋調用了RunWorkerAsync方法就會觸發DoWork事件的發生啊? 對於這個疑惑,我很快會為大家明白,這一切的一切又是委托在起作用了(所以我一直認為,微軟的幾乎所有特性都是基於委托來實現的,然而委托又是方法的包裝,具體可以參看的委托專題. 從而又追根到底就是方法了)。
.threadStart = WorkerThreadStartDelegate( .operationCompleted = SendOrPostCallback(.progressReporter = SendOrPostCallback(
3. threadStart包裝了WorkerThreadStart方法,從而解決了第一步的疑惑,接下來就讓我們看看WorkerThreadStart方法的代碼:
WorkerThreadStart( result = = cancelled = = 該方法中又是調用了onDoWork方法 = == RunWorkerCompletedEventArgs arg = .asyncOperation.PostOperationCompleted(
4. WorkerThreadStart調用了受保護的OnDoWork方法,下面就讓我們看看OnDoWork方法的代碼,到這裡我們離事物的本質已經不遠了。
DoWorkEventHandler handler = (DoWorkEventHandler) (handler != // 到這裡就可以解釋為什麼調用RunWorkerAsync方法會觸發DoWork事件了 handler(
從上面的代碼中的注釋我們可以解釋一開始的疑惑,並且也更好地解釋了事件特性,關於事件,你也可以參看我的事件專題(事件也是委托,歸根究底又是委托啊,從而可見委委托是多麼的重要,同時建議大家在理解委托的時候,可以根據後面的特性重復地去理解)。
對於開始表格中提出的其他的幾個疑惑的分析思路和這個分析思路類似,大家可以按照這個思路自己去深入理解下BackgroundWorker類,這裡我就不多解釋了。相信大家通過上面我的分析可以很快解決其他幾個疑惑的,如果你完全理解上面的分析相信你會對EAP,委托和事件又有進一步的理解。
剖析完了BackgroundWorker組件之後,我們是不是很想看看如何使用這個類來實現異步編程呢?下面向大家演示一個使用BackgroundWorker組件實現異步下載文件的一個小程序,該程序支持異步下載(指的就是用線程池線程要執行下載操作),斷點續傳、下載取消和進度報告的功能,通過這個程序,相信大家也會對基於事件的異步模式有一個更好的理解和知道該模式可以完成一些什麼樣的任務,下面就看看該程序的主要代碼的(因為代碼中都有詳細的解釋,這裡就不多解釋代碼的實現了):
btnDownload_Click( (bgWorkerFileDownload.IsBusy != requestState = .btnDownload.Enabled = .btnPause.Enabled = btnPause_Click( (bgWorkerFileDownload.IsBusy&&bgWorkerFileDownload.WorkerSupportsCancellation == BackGroundWorker Event bgWorkerFileDownload_DoWork( BackgroundWorker bgworker = sender HttpWebRequest myHttpWebRequest = (DownloadSize != requestState.request === readSize = ( (bgworker.CancellationPending == = = requestState.streamResponse.Read(requestState.BufferRead, (readSize > += percentComplete = ()(()DownloadSize / ()totalSize * bgWorkerFileDownload_ProgressChanged(.progressBar1.Value = bgWorkerFileDownload_RunWorkerCompleted( (e.Error != .btnDownload.Enabled = .btnPause.Enabled = .btnDownload.Enabled = .btnPause.Enabled = === BufferSize = RequestState(= = = =
運行程序點擊"下載"按鈕然後再點擊"暫停"後的結果:
當暫停下載後,我們還可以點 ”下載“按鈕繼續下載該文件,此時並不會從開開始下載,而會接著上次的下載繼續下載(這個實現主要是通過AddRange方法來實現的,該方法是指出向服務器請求文件的大小,上面代碼中通過傳入DownloadSize來告訴服務器,這次我需要的內容不是從開頭開始的,而是從已經下載的文件字節數開始到該文件的總的字節結尾,這樣就就實現了斷點續傳的功能了,使戶暫停下載不至於之前下載的都白費了。),程序的運行結果為:
到這裡,本專題的內容就介紹完了,本專題主要介紹.NET 2.0中提出的新的異步編程模式——基於事件的異步編程模式,相信通過本專題的介紹,你將對EAP有一定的了解,並且對BackgroundWorker組件、委托和事件也會有深入地的理解,而不再停留在只會使用該組件階段,而是到達”會知其然之氣所以然“的一個階段。後面的一個專題將會為大家介紹。NET 4.0中提出的最新的異步編程模式,也是進行異步編程推薦的一種編程模式,即基於任務的編程模式(TAP)。
本專題源碼下載地址:http://files.cnblogs.com/zhili/Event-basedAsynchronousPattern.zip