1、異步編程模型(APM)html
2、基於事件的異步編程模式(EAP)spring
在上一個專題中爲你們介紹了.NET 1.0中提出來的異步編程模式——APM,雖然APM爲咱們實現異步編程提供了必定的支持,同時它也存在着一些明顯的問題——不支持對異步操做的取消和沒有提供對進度報告的功能,對於有界面的應用程序來講,進度報告和取消操做的支持也是必不可少的,既然存在這樣的問題,微軟固然也應該提供給咱們解決問題的方案了,因此微軟在.NET 2.0的時候就爲咱們提供了一個新的異步編程模型,也就是我這個專題中介紹的基於事件的異步編程模型——EAP(Event-based Asynchronous Pattern )。服務器
實現了基於事件的異步模式的類將具備一個或者多個以Async爲後綴的方法和對應的Completed事件,而且這些類都支持異步方法的取消、進度報告和報告結果。異步
當咱們調用實現基於事件的異步模式的類的 XxxAsync方法時,即表明開始了一個異步操做,該方法調用完以後會使一個線程池線程去執行耗時的操做,因此當UI線程調用該方法時,固然也就不會堵塞UI線程了。而且基於事件的異步模式是創建了APM的基礎之上的(這也是我在上一專題中詳解介紹APM的緣由),而APM又是創建了在委託之上的(對於這點能夠參考該系列的APM專題)。BackgroundWorker類就是用EAP實現的。ide
public static void GetInfomation() { WebClient client = new WebClient(); Uri uri = new Uri("http://www.baidu.com"); client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted); client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressCallback); client.DownloadFileAsync(uri, "serverdata.txt"); } static void client_DownloadProgressCallback(object sender, DownloadProgressChangedEventArgs e) { // Displays the operation identifier, and the transfer progress. Console.WriteLine("{0} downloaded {1} of {2} bytes. {3} % complete...", (string)e.UserState, e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage); } static void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { string RetStr = (e.Error == null ? "成功" : e.Error.Message); Console.Write(RetStr); //顯示操做結果信息 }
下面向你們演示一個使用BackgroundWorker組件實現異步下載文件的一個小程序,該程序支持異步下載(指的就是用線程池線程要執行下載操做),斷點續傳、下載取消和進度報告的功能,經過這個程序,相信你們也會對基於事件的異步模式有一個更好的理解和知道該模式能夠完成一些什麼樣的任務,下面就看看該程序的主要代碼的(由於代碼中都有詳細的解釋,這裏就很少解釋代碼的實現了):異步編程
public FileDownloader() { InitializeComponent(); string url = "http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe"; //string url = "http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe"; txbUrl.Text = url; this.btnPause.Enabled = false; GetTotalSize(); downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + Path.GetFileName(this.txbUrl.Text.Trim()); if (File.Exists(downloadPath)) { FileInfo fileInfo = new FileInfo(downloadPath); DownloadSize = (int)fileInfo.Length; progressBar1.Value = (int)((float)DownloadSize / (float)totalSize * 100); } //啓用支持 ReportProgress and Cancellation bgWorkerFileDownload.WorkerReportsProgress = true; bgWorkerFileDownload.WorkerSupportsCancellation = true; } //開始或恢復下載 private void btnDownload_Click(object sender, EventArgs e) { if (bgWorkerFileDownload.IsBusy != true) { // 啓動異步操做觸發DoWork事件 bgWorkerFileDownload.RunWorkerAsync(); // 建立RequestState 實例 requestState = new RequestState(downloadPath); requestState.filestream.Seek(DownloadSize, SeekOrigin.Begin); this.btnDownload.Enabled = false; this.btnPause.Enabled = true; } else { MessageBox.Show("正在執行操做,請稍後"); } } // 暫停下載 private void btnPause_Click(object sender, EventArgs e) { if (bgWorkerFileDownload.IsBusy && bgWorkerFileDownload.WorkerSupportsCancellation == true) { //終止異步操做觸發RunWorkerCompleted事件 bgWorkerFileDownload.CancelAsync(); } } #region BackGroundWorker Event //當調用RunWorkerAsync方法時觸發 private void bgWorkerFileDownload_DoWork(object sender, DoWorkEventArgs e) { // Get the source of event BackgroundWorker bgworker = sender as BackgroundWorker; try { HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim()); //若是文件已下載了一部分, // The server should start sending data from the DownloadSize to the end of the data in the HTTP entity. if (DownloadSize != 0) { myHttpWebRequest.AddRange(DownloadSize); } if (DownloadSize >= totalSize) { MessageBox.Show("已經下載完畢"); return; } requestState.request = myHttpWebRequest; requestState.response = (HttpWebResponse)myHttpWebRequest.GetResponse(); requestState.streamResponse = requestState.response.GetResponseStream(); int readSize = 0; while (true) { if (bgworker.CancellationPending == true) { e.Cancel = true; break; } readSize = requestState.streamResponse.Read(requestState.BufferRead, 0, requestState.BufferRead.Length); if (readSize > 0) { DownloadSize += readSize; int percentComplete = (int)((float)DownloadSize / (float)totalSize * 100); requestState.filestream.Write(requestState.BufferRead, 0, readSize); // 報告進度,引起ProgressChanged事件的發生 bgworker.ReportProgress(percentComplete); } else { break; } } } catch { throw; } } // 當調用ReportProgress方法時觸發 private void bgWorkerFileDownload_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.progressBar1.Value = e.ProgressPercentage; } //當後臺操做完成,取消或者有錯誤時觸發。 private void bgWorkerFileDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.Message); requestState.response.Close(); } else if (e.Cancelled) { MessageBox.Show(String.Format("下載暫停,下載的文件地址爲:{0}\n 已經下載的字節數爲: {1}字節", downloadPath, DownloadSize)); requestState.response.Close(); requestState.filestream.Close(); this.btnDownload.Enabled = true; this.btnPause.Enabled = false; } else { MessageBox.Show(String.Format("下載已完成,下載的文件地址爲:{0},文件的總字節數爲: {1}字節", downloadPath, totalSize)); this.btnDownload.Enabled = false; this.btnPause.Enabled = false; requestState.response.Close(); requestState.filestream.Close(); } } #endregion BackGroundWorker Event // 獲取文件總大小 private void GetTotalSize() { HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim()); HttpWebResponse response = (HttpWebResponse)myHttpWebRequest.GetResponse(); totalSize = response.ContentLength; response.Close(); }
運行程序點擊"下載"按鈕而後再點擊"暫停"後的結果:post
當暫停下載後,咱們還能夠點 」下載「按鈕繼續下載該文件,此時並不會從開開始下載,而會接着上次的下載繼續下載(這個實現主要是經過AddRange方法來實現的,該方法是指出向服務器請求文件的大小,上面代碼中經過傳入DownloadSize來告訴服務器,此次我須要的內容不是從開頭開始的,而是從已經下載的文件字節數開始到該文件的總的字節結尾,這樣就就實現了斷點續傳的功能了,使戶暫停下載不至於以前下載的都白費了。),程序的運行結果爲:this
到這裏,本專題的內容就介紹完了,本專題主要介紹.NET 2.0中提出的新的異步編程模式——基於事件的異步編程模式,相信經過本專題的介紹,你將對EAP有必定的瞭解,而且對BackgroundWorker組件、委託和事件也會有深刻地的理解,而再也不停留在只會使用該組件階段,而是到達」會知其然之氣因此然「的一個階段。後面的一個專題將會爲你們介紹。NET 4.0中提出的最新的異步編程模式,也是進行異步編程推薦的一種編程模式,即基於任務的編程模式(TAP)。