一、概述

前面我们了解到了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 }
<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

异步编程之EAP的更多相关文章

  1. 异步编程之Generator(1)——领略魅力

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  2. 异步编程之Promise(3):拓展进阶

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  3. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  4. (翻译)异步编程之Promise(1):初见魅力

    原文:https://www.promisejs.org/ by Forbes Lindesay 异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2) ...

  5. net异步编程之await

    net异步编程之await 初探asp.net异步编程之await   终于毕业了,也顺利进入一家期望的旅游互联网公司.27号入职.放肆了一个多月没写代码,好方啊. 另外一下观点均主要针对于await ...

  6. Javascript异步编程之setTimeout与setInterval详解分析(一)

    Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...

  7. 异步编程之co——源码分析

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  8. 异步编程之Generator(2)——剖析特性

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  9. 【转】Javascript异步编程之setTimeout与setInterval

    Javascript异步编程之setTimeout与setInterval 转自:http://www.tuicool.com/articles/Ebueua 在谈到异步编程时,本人最主要会从以下三个 ...

随机推荐

  1. C语言:预处理 自定义头文件

    DEV-C++包含文件搜索路径C:\Program Files\Dev-Cpp\MinGW64\x86_64-w64-mingw32\includeC:\Program Files\Dev-Cpp\M ...

  2. Windows内核开发-3-内核编程基础

    Windows内核开发-3-内核编程基础 这里会深入讲解kernel内核的API.结构体.和一些定义.考察代码在内核驱动中运行的机制.最后把所有知识合在一起写一个有用的驱动. 本章学习要点: 1:通用 ...

  3. Unittest方法 -- 测试断言

    """断言详解"""from unittest_1.it import *def add(a,b): return a - bclass B ...

  4. SQL慢查询排查思路

    前言 平时在工作中每天都会做巡检,将前一天所有超过500ms的慢SQL排查出来 查找原因,是否能进行优化.慢慢中,在形成了一套思路方法论. 我个人认为对于排查慢SQL还是有一定的帮助 (一).是否是S ...

  5. 动态 WebApi 引擎使用教程(3行代码完成动态 WebApi 构建)

    目录 什么是 WebApiEngine? 开源地址 使用方法 使用 [ApiBind] 标签让任何方法变成 WebApi 对 API 进行分类 自定义 API 名称 复制特性 为整个类配置 WebAp ...

  6. 第二十九篇 -- UDP和TCP

    最近在写WIFI模块,所以就想明确一些TCP和UDP的区别,发现以前的理解还是有点误区.现在重新学习. 相同点 UDP协议和TCP协议都是传输层协议 TCP(Transmission Control ...

  7. Altium Designer 21.x中文版安装破解教程

    Altium Designer 21.x是一款优秀的PCB设计工具,可以原理图设计.电路仿真.PCB绘制编辑.拓扑逻辑自动布线.信号完整性分析和设计输出等功能,为设计者提供了全新的设计解决方案,提高设 ...

  8. 离线webpack创建vue 项目

    参考地址: https://blog.csdn.net/feinifi/article/details/104578546 画重点: // 需要带上参数--offline表示离线初始化. --offl ...

  9. js 原始数据类型、引用数据类型

    js的数据类型划分方式为 原始数据类型和 引用数据类型 栈: 原始数据类型(Undefined,Null,Boolean,Number.String) 堆: 引用数据类型(对象.数组.函数) 两种类型 ...

  10. 【死磕 Java 基础】 — 谈谈那个写时拷贝技术(copy-on-write)

    copy-on-write,即写时复制技术,这是小编在学习 Redis 持久化时看到的一个概念,当然在这个概念很早就碰到过(Java 容器并发有这个概念),但是一直都没有深入研究过,所以趁着这次机会对 ...