异步编程之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.Media.SoundPlay

System.Net.WebClient

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 }
View Code

 

<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>
View Code


注:本文参考https://www.cnblogs.com/zhili/archive/2013/05/11/EAP.html

 

posted on 2019-02-14 16:22  缘惜  阅读(538)  评论(0编辑  收藏  举报