异步编程之EAP
一、概述
前面我们了解到了APM编程模式,但APM不支持对异步操作的取消和没有提供对进度报告的功能。
对于界面程序来说,进度报告和取消操作的支持也是必不可少的,为了支持这些功能,微软在.NET 2.0的时候提出了一个新的异步编程模型---基于事件的异步编程模型——EAP。
实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步方法的取消、进度报告。
然而在.NET类库中并不是所有的类都支持EAP的,可能有朋友会误认为是不是支持APM的类都支持EAP的呢?在.NET 类库中只有部分的类支持EAP的(并且也只有部分类支持APM),这些类有(共17个类):
System.Object的派生类型:
System.Activies.WorkflowInvoke
System.Deployment.Application.ApplicationDeployment
System.Deployment.Application.InPlaceHosingManager
System.Net.Mail.SmtpClient
System.Net.PeerToPeer.PeerNameResolver
System.Net.PeerToPeer.Collaboration.ContactManager
System.Net.PeerToPeer.Collaboration.Peer
System.Net.PeerToPeer.Collaboration.PeerContact
System.Net.PeerToPeer.Collaboration.PeerNearMe
System.ServiceModel.Activities.WorkflowControlClient
System.ServiceModel.Discovery.AnnoucementClient
System.ServiceModel.Discovery.DiscoveryClient
System.ComponentModel.Component的派生类型:
System.ComponentModel.BackgroundWorker
System.Net.NetworkInformation.Ping
System.Windows.Forms.PictureBox(继承于Control类,Control类派生于Component类)
当我们调用实现基于事件的异步模式的类的 XxxAsync方法时,即代表开始了一个异步操作,该方法调用完之后会使一个线程池线程去执行耗时的操作。
二、Demo
下面以BackgroundWorker类制作一个下载文件的demo。
BackgroundWorker类关键点
调用函数RunWorkerAsync时,会触发DoWork事件;
调用函数ReportProgress时,会触发ProgressChanged事件;
当后台操作已完成、被取消或引发异常时发生时,会触发RunWorkerCompleted事件。
1 using System; 2 using System.ComponentModel; 3 using System.IO; 4 using System.Net; 5 using System.Threading; 6 using System.Windows; 7 8 namespace Wpf_EAP 9 { 10 public class RequestState 11 { 12 private HttpWebRequest request; 13 public HttpWebRequest Request 14 { 15 get 16 { 17 return request; 18 } 19 set 20 { 21 request = value; 22 } 23 } 24 private HttpWebResponse response; 25 public HttpWebResponse Response 26 { 27 get 28 { 29 return response; 30 } 31 set 32 { 33 response = value; 34 } 35 } 36 public Stream ResponseStream; 37 public FileStream Filestream = null; 38 39 public byte[] BufferRead = new byte[1024]; 40 public static int Index = 1; 41 public RequestState(string fileSavePath) 42 { 43 //string fileName = "Pic" + (Index++).ToString(); 44 string fileName = "Pic"; 45 string saveFilePath = fileSavePath + fileName + ".jpg";//以下载jpg图片为例 46 //if(File.Exists(saveFilePath)) 47 //{ 48 // File.Delete(saveFilePath); 49 //} 50 Filestream = new FileStream(saveFilePath, FileMode.OpenOrCreate); 51 } 52 } 53 /// <summary> 54 /// Interaction logic for MainWindow.xaml 55 /// </summary> 56 public partial class MainWindow : Window 57 { 58 private string downLoadUrl = @"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2298824648,1812234339&fm=200&gp=0.jpg"; 59 public string DownLoadUrl 60 { 61 get { return downLoadUrl; } 62 set { downLoadUrl = value; } 63 } 64 private string fileSavePath = @"D:\360Downloads\"; 65 public string FileSavePath 66 { 67 get { return fileSavePath; } 68 set { fileSavePath = value; } 69 } 70 private int downLoadSize = 0; 71 private BackgroundWorker bgWorker; 72 private RequestState requestState; 73 private long totalSize = 0; 74 public MainWindow() 75 { 76 InitializeComponent(); 77 this.DataContext = this; 78 bgWorker = new BackgroundWorker(); 79 bgWorker.WorkerSupportsCancellation = true; 80 bgWorker.WorkerReportsProgress = true; 81 bgWorker.DoWork += bgWorkerFileDownload_DoWork; 82 bgWorker.ProgressChanged += bgWorkerFileDownload_ProgressChanged; 83 bgWorker.RunWorkerCompleted += bgWorkerFileDownload_RunWorkerCompleted; 84 85 } 86 private void GetTotalSize() 87 { 88 HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(DownLoadUrl); 89 HttpWebResponse response = (HttpWebResponse)myHttpWebRequest.GetResponse(); 90 totalSize = response.ContentLength; 91 response.Close(); 92 } 93 private void bgWorkerFileDownload_DoWork(object sender, DoWorkEventArgs e) 94 { 95 BackgroundWorker bgworker = sender as BackgroundWorker; 96 try 97 { 98 GetTotalSize(); 99 // Do the DownLoad operation 100 // Initialize an HttpWebRequest object 101 HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(DownLoadUrl); 102 103 // If the part of the file have been downloaded, 104 // The server should start sending data from the DownloadSize to the end of the data in the HTTP entity. 105 if (downLoadSize != 0) 106 { 107 myHttpWebRequest.AddRange(downLoadSize); 108 } 109 110 // assign HttpWebRequest instance to its request field. 111 requestState.Request = myHttpWebRequest; 112 requestState.Response = (HttpWebResponse)myHttpWebRequest.GetResponse(); 113 requestState.ResponseStream = requestState.Response.GetResponseStream(); 114 int readSize = 0; 115 while (true) 116 { 117 if (bgworker.CancellationPending == true) 118 { 119 e.Cancel = true; 120 break; 121 } 122 123 readSize = requestState.ResponseStream.Read(requestState.BufferRead, 0, requestState.BufferRead.Length); 124 if (readSize > 0) 125 { 126 downLoadSize += readSize; 127 int percentComplete = (int)((float)downLoadSize / (float)totalSize * 100); 128 requestState.Filestream.Write(requestState.BufferRead, 0, readSize); 129 Thread.Sleep(100); 130 // 报告进度,引发ProgressChanged事件的发生 131 bgworker.ReportProgress(percentComplete); 132 } 133 else 134 { 135 break; 136 } 137 } 138 } 139 catch(Exception err) 140 { 141 MessageBox.Show(err.Message); 142 } 143 } 144 private void bgWorkerFileDownload_ProgressChanged(object sender, ProgressChangedEventArgs e) 145 { 146 //progressBar.Value = e.ProgressPercentage; 147 Dispatcher.BeginInvoke(new Action( ()=> { progressBar.Value = e.ProgressPercentage; } )); 148 } 149 private void bgWorkerFileDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 150 { 151 if (e.Error != null) 152 { 153 MessageBox.Show(e.Error.Message); 154 requestState.Response.Close(); 155 } 156 else if (e.Cancelled) 157 { 158 int percentComplete = (int)((float)downLoadSize / (float)totalSize * 100); 159 MessageBox.Show(String.Format("下载暂停,下载的文件地址为:{0}\n 已经下载的字节数/总字节: {1}字节/{2} 百分比:{3} %", DownLoadUrl, downLoadSize, totalSize,percentComplete)); 160 requestState.Response.Close(); 161 requestState.Filestream.Close(); 162 163 this.btnDownLoad.IsEnabled = true; 164 this.btnPause.IsEnabled = false; 165 } 166 else 167 { 168 MessageBox.Show(String.Format("下载已完成,下载的文件地址为:{0},文件的总字节数为: {1}字节", DownLoadUrl, totalSize)); 169 downLoadSize = 0; 170 Dispatcher.BeginInvoke(new Action(() => { progressBar.Value = 0; })); 171 this.btnDownLoad.IsEnabled = true; 172 this.btnPause.IsEnabled = false; 173 requestState.Response.Close(); 174 requestState.Filestream.Close(); 175 } 176 177 } 178 179 private void btnPause_Click(object sender, RoutedEventArgs e) 180 { 181 if (bgWorker.IsBusy && bgWorker.WorkerSupportsCancellation == true) 182 { 183 // Pause the asynchronous operation 184 // Fire RunWorkerCompleted event 185 bgWorker.CancelAsync(); 186 } 187 } 188 189 private void btnDownLoad_Click(object sender, RoutedEventArgs e) 190 { 191 if (bgWorker.IsBusy != true) 192 { 193 bgWorker.RunWorkerAsync();//触发DoWork事件 194 // Create an instance of the RequestState 195 requestState = new RequestState(FileSavePath); 196 requestState.Filestream.Seek(downLoadSize, SeekOrigin.Begin); 197 this.btnDownLoad.IsEnabled = false; 198 this.btnPause.IsEnabled = true; 199 } 200 else 201 { 202 MessageBox.Show("下载进行中,请稍后..."); 203 } 204 } 205 } 206 }
<Window x:Class="Wpf_EAP.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Wpf_EAP" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="50"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <Label Content="DownLoadUrl:" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Right"></Label> <Label Content="FileSavePath:" FontSize="14" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Right"></Label> <TextBox FontSize="20" BorderBrush="Green" BorderThickness="1" Grid.Column="1" Margin="5" Grid.Row="1" Text="{Binding FileSavePath}" Name="tbSavePath"/> <TextBox Text="{Binding DownLoadUrl}" FontSize="20" BorderBrush="Green" BorderThickness="1" Name="lbUrl" Grid.Column="1" Margin="5"/> <Button Content="DownLoad" Grid.Column="2" Grid.Row="0" FontSize="20" Name="btnDownLoad" VerticalAlignment="Center" Click="btnDownLoad_Click"/> <Button Content="Pause" Grid.Column="2" Grid.Row="1" FontSize="20" Name="btnPause" VerticalAlignment="Center" Click="btnPause_Click"/> <ProgressBar Grid.Row="2" Grid.ColumnSpan="3" Height="30" Name="progressBar" Minimum="0" Maximum="100"></ProgressBar> </Grid> </Window>
注:本文参考https://www.cnblogs.com/zhili/archive/2013/05/11/EAP.html