接着上一篇,我们继续来优化。我们的 SkyParallelWebClient 可否支持切换“同步下载模式”和“异步下载模式”呢,好处是大量的代码不用改,只需要调用 skyParallelWebClient.StartAsync() 就开始异步下载,而改为 skyParallelWebClient.StartSync(); 就同步下载。如图:

同步下载:

异步下载:

1. 同步下载模式

直接贴代码了:

    public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private List<int> GetDownloadIds()
{
List<int> ids = new List<int>();
for (int i = ; i <= ; i++)
{
ids.Add(i);
}
return ids;
} private void WhenAllDownloading()
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
//禁用按钮
EnableOrDisableButtons(false);
} private void EnableOrDisableButtons(bool enabled)
{
this.btnRunByHttpClient.Enabled = enabled;
this.btnRunByWebClient.Enabled = enabled;
} private void WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
} private void WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 下载完毕...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
} private void WhenAllDownloaded()
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},下载完毕!", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
//启用按钮
EnableOrDisableButtons(true);
} private async void btnRunByHttpClient_Click(object sender, EventArgs e)
{
//SkyHttpClient skyHttpClient = new SkyHttpClient();
//try
//{
// WhenAllDownloading();
// foreach (var id in GetDownloadIds())
// {
// bool singleDownloadSuccess = await TaskDemo101.RunByHttpClient(skyHttpClient, id);
// WhenSingleDownloading(id);
// }
// WhenAllDownloaded();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.Message, "Download Error!");
//}
} private void btnRunByWebClient_Click(object sender, EventArgs e)
{
WhenAllDownloading();
var ids = GetDownloadIds();
List<DownloadEntry> downloadConfigs = new List<DownloadEntry>();
Random rd = new Random();
foreach (var id in ids)
{
downloadConfigs.Add(new DownloadEntry(TaskDemo101.GetRandomUrl(rd), TaskDemo101.GetSavedFileFullName(), id));
}
// 方案4(同步下载,需要启动另外一个线程)
ThreadPool.QueueUserWorkItem(new WaitCallback(RunByWebClientCore), downloadConfigs);
} private void RunByWebClientCore(object state)
{
List<DownloadEntry> downloadConfigs = (List<DownloadEntry>)state;
SkyParallelWebClient skyParallelWebClient = new SkyParallelWebClient(downloadConfigs, );
skyParallelWebClient.SetMaximumForProgress += SkyParallelWebClient_SetMaximumForProgress;
skyParallelWebClient.SetRealTimeValueForProgress += SkyParallelWebClient_SetRealTimeValueForProgress;
skyParallelWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
skyParallelWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
skyParallelWebClient.WhenSingleDownloaded += SkyWebClient_WhenSingleDownloaded;
skyParallelWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
skyParallelWebClient.StartSync();
} private void SkyParallelWebClient_SetRealTimeValueForProgress(int realTimeValue)
{
this.progressBar1.Value = realTimeValue;
} private void SkyParallelWebClient_SetMaximumForProgress(int maximum)
{
this.progressBar1.Maximum = maximum;
} private void SkyWebClient_WhenDownloadingError(Exception ex)
{
MessageBox.Show("下载时出现错误: " + ex.Message);
} private void SkyWebClient_WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
{
WhenSingleDownloading(eventArg);
} private void SkyWebClient_WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
{
WhenSingleDownloaded(eventArg);
} private void SkyWebClient_WhenAllDownloaded()
{
btnRunByWebClient.Text = "用 WebClient 开始下载";
WhenAllDownloaded();
} }

运行截图:

2. 异步下载模式

直接贴代码了:

    public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private List<int> GetDownloadIds()
{
List<int> ids = new List<int>();
for (int i = ; i <= ; i++)
{
ids.Add(i);
}
return ids;
} private void WhenAllDownloading()
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
//禁用按钮
EnableOrDisableButtons(false);
} private void EnableOrDisableButtons(bool enabled)
{
this.btnRunByHttpClient.Enabled = enabled;
this.btnRunByWebClient.Enabled = enabled;
} private void WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
} private void WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 下载完毕...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
} private void WhenAllDownloaded()
{
this.listBoxLog.Items.Insert(, string.Format("当前时间:{0},下载完毕!", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
//启用按钮
EnableOrDisableButtons(true);
} private async void btnRunByHttpClient_Click(object sender, EventArgs e)
{
//SkyHttpClient skyHttpClient = new SkyHttpClient();
//try
//{
// WhenAllDownloading();
// foreach (var id in GetDownloadIds())
// {
// bool singleDownloadSuccess = await TaskDemo101.RunByHttpClient(skyHttpClient, id);
// WhenSingleDownloading(id);
// }
// WhenAllDownloaded();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.Message, "Download Error!");
//}
} private void btnRunByWebClient_Click(object sender, EventArgs e)
{
WhenAllDownloading();
var ids = GetDownloadIds();
List<DownloadEntry> downloadConfigs = new List<DownloadEntry>();
Random rd = new Random();
foreach (var id in ids)
{
downloadConfigs.Add(new DownloadEntry(TaskDemo101.GetRandomUrl(rd), TaskDemo101.GetSavedFileFullName(), id));
}
//搜索: Parallel WebClient
// 方案1
//SkyWebClient skyWebClient = new SkyWebClient(downloadConfigs, this.progressBar1);
//skyWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
//skyWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
//skyWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
//skyWebClient.Start();
// 方案2(代码已经调整,无法恢复)
//ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncRunSkyParallelWebClient), downloadConfigs);
// 方案3(异步下载,完美)
SkyParallelWebClient skyParallelWebClient = new SkyParallelWebClient(downloadConfigs, );
skyParallelWebClient.SetMaximumForProgress += SkyParallelWebClient_SetMaximumForProgress;
skyParallelWebClient.SetRealTimeValueForProgress += SkyParallelWebClient_SetRealTimeValueForProgress;
skyParallelWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
skyParallelWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
skyParallelWebClient.WhenSingleDownloaded += SkyWebClient_WhenSingleDownloaded;
skyParallelWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
skyParallelWebClient.StartSync();
// 方案4(同步下载,需要启动另外一个线程)
//ThreadPool.QueueUserWorkItem(new WaitCallback(RunByWebClientCore), downloadConfigs);
} private void RunByWebClientCore(object state)
{
List<DownloadEntry> downloadConfigs = (List<DownloadEntry>)state;
SkyParallelWebClient skyParallelWebClient = new SkyParallelWebClient(downloadConfigs, );
skyParallelWebClient.SetMaximumForProgress += SkyParallelWebClient_SetMaximumForProgress;
skyParallelWebClient.SetRealTimeValueForProgress += SkyParallelWebClient_SetRealTimeValueForProgress;
skyParallelWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
skyParallelWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
skyParallelWebClient.WhenSingleDownloaded += SkyWebClient_WhenSingleDownloaded;
skyParallelWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
skyParallelWebClient.StartSync();
} private void SkyParallelWebClient_SetRealTimeValueForProgress(int realTimeValue)
{
this.progressBar1.Value = realTimeValue;
} private void SkyParallelWebClient_SetMaximumForProgress(int maximum)
{
this.progressBar1.Maximum = maximum;
} private void SkyWebClient_WhenDownloadingError(Exception ex)
{
MessageBox.Show("下载时出现错误: " + ex.Message);
} private void SkyWebClient_WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
{
WhenSingleDownloading(eventArg);
} private void SkyWebClient_WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
{
WhenSingleDownloaded(eventArg);
} private void SkyWebClient_WhenAllDownloaded()
{
btnRunByWebClient.Text = "用 WebClient 开始下载";
WhenAllDownloaded();
} }

运行截图:

如上图:当打印了“下载完毕”后,仍然打印了许多日志,这是因为异步下载的回调是不定时的。为了避免这种情况,建议 WhenSingleDownloading 事件和 WhenSingleDownloaded 事件二选一,因为实在没有必要下载开始前和下载后都打印日志。我们再次修改代码后,得到如下图的日志。

日志是不是清晰了很多?一般下载完成,可以注册完成事件,事件里面不用打印日志,而做一些比如“修改数据库表记录的状态字段”等等。

3. 总结

SkyParallelWebClient 完整的代码如下:

    /// <summary>
/// 设置进度条的最大值的事件处理
/// </summary>
/// <param name="maximum"></param>
public delegate void SetMaximumForProgressEventHandler(int maximum); /// <summary>
/// 设置进度条的当前实时的值的事件处理
/// </summary>
/// <param name="maximum"></param>
public delegate void SetRealTimeValueForProgressEventHandler(int realTimeValue); /// <summary>
/// 并行的 WebClient
/// </summary>
public class SkyParallelWebClient : SkyWebClientBase
{
ConcurrentQueue<DownloadEntry> OptionDataList = new ConcurrentQueue<DownloadEntry>(); //比如说:有 500 个元素 ConcurrentDictionary<WebClient, DownloadEntry> ProcessingTasks = new ConcurrentDictionary<WebClient, DownloadEntry>(); //当前运行中的 public event SetMaximumForProgressEventHandler SetMaximumForProgress;
protected virtual void OnSetMaximumForProgress(int maximum)
{
if (SetMaximumForProgress != null)
{
SetMaximumForProgress(maximum);
}
} public event SetRealTimeValueForProgressEventHandler SetRealTimeValueForProgress;
protected virtual void OnSetRealTimeValueForProgress(int realTimeValue)
{
if (SetRealTimeValueForProgress != null)
{
SetRealTimeValueForProgress(realTimeValue);
}
} public int ParallelCount { get; } public bool IsCompleted { get; private set; } public int Maximum { get;} private static object lockObj = new object(); /// <summary>
/// 构造函数
/// </summary>
/// <param name="downloadConfigs">要下载的全部集合,比如 N 多要下载的,N无限制</param>
public SkyParallelWebClient(IEnumerable<DownloadEntry> downloadConfigs)
:this(downloadConfigs, )
{ } /// <summary>
/// 构造函数
/// </summary>
/// <param name="downloadConfigs">要下载的全部集合,比如 N 多要下载的,N无限制</param>
/// <param name="parallelCount">单位内,并行下载的个数。切忌:该数字不能过大,否则可能很多文件因为 WebClient 超时,而导致乱文件(即:文件不完整)一般推荐 20 个左右</param>
public SkyParallelWebClient(IEnumerable<DownloadEntry> downloadConfigs, int parallelCount)
{
if (downloadConfigs == null)
{
throw new ArgumentNullException("downloadConfigs");
}
this.ParallelCount = parallelCount;
foreach (var item in downloadConfigs)
{
OptionDataList.Enqueue(item);
}
this.Maximum = OptionDataList.Count;
System.Net.ServicePointManager.DefaultConnectionLimit = int.MaxValue;
OnSetMaximumForProgress(this.Maximum);
} /// <summary>
/// 异步启动(备注:由于内部采用异步下载,所以方法不用加 Try 和返回值)
/// </summary>
public void StartAsync()
{
StartAsyncCore();
} protected void StartAsyncCore()
{
if (OptionDataList.Count <= )
{
SetIsCompletedTrue();
return;
}
while (OptionDataList.Count > && ProcessingTasks.Count <= ParallelCount)
{
DownloadEntry downloadEntry;
if (!OptionDataList.TryDequeue(out downloadEntry))
{
break;
}
DownloadFileAsync(downloadEntry);
OnWhenSingleDownloading(new WhenSingleDownloadEventArgs
{
CurrentDownloadEntry = downloadEntry,
RemainingQueueCount = OptionDataList.Count
});
}
} /// <summary>
/// 同步启动
/// </summary>
public void StartSync()
{
StartSyncCore();
} /// <summary>
/// 同步启动
/// </summary>
public void StartSync(WebClient webClient)
{
StartSyncCore(webClient);
} protected void StartSyncCore()
{
using (WebClient webClient = new WebClient())
{
StartSyncCore(webClient);
}
} protected void StartSyncCore(WebClient webClient)
{
while (OptionDataList.Count > )
{
DownloadEntry downloadEntry;
if (!OptionDataList.TryDequeue(out downloadEntry))
{
break;
}
OnWhenSingleDownloading(new WhenSingleDownloadEventArgs
{
CurrentDownloadEntry = downloadEntry,
RemainingQueueCount = OptionDataList.Count
});
DownloadFile(downloadEntry, webClient);
OnWhenSingleDownloaded(new WhenSingleDownloadEventArgs
{
CurrentDownloadEntry = downloadEntry,
RemainingQueueCount = OptionDataList.Count
});
}
SetIsCompletedTrue();
} protected void SetIsCompletedTrue()
{
if (!IsCompleted)
{
lock (lockObj)
{
if (!IsCompleted)
{
OnSetRealTimeValueForProgress();//表示已经完成
OnWhenAllDownloaded();
IsCompleted = true;
}
}
}
} private void DownloadFileAsync(DownloadEntry downloadEntry)
{
using (WebClient webClient = new WebClient())
{
webClient.Proxy = null;
webClient.DownloadFileCompleted += WebClient_DownloadFileCompleted;
webClient.DownloadFileAsync(new Uri(downloadEntry.Url), downloadEntry.Path);
ProcessingTasks.TryAdd(webClient, downloadEntry);
}
} private void DownloadFile(DownloadEntry downloadEntry)
{
using (WebClient webClient = new WebClient())
{
DownloadFile(downloadEntry, webClient);
}
} private void DownloadFile(DownloadEntry downloadEntry, WebClient webClient)
{
try
{
webClient.Proxy = null;
webClient.DownloadFile(new Uri(downloadEntry.Url), downloadEntry.Path);
}
catch (Exception ex)
{
OnWhenDownloadingError(ex);
}
} private void WebClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
WebClient webClient = (WebClient)sender;
DownloadEntry downloadEntry;
if (ProcessingTasks.TryRemove(webClient, out downloadEntry))
{
OnWhenSingleDownloaded(new WhenSingleDownloadEventArgs
{
CurrentDownloadEntry = downloadEntry,
RemainingQueueCount = OptionDataList.Count
});
try
{
int realTimeValue = (this.Maximum - OptionDataList.Count) * / this.Maximum;
OnSetRealTimeValueForProgress(realTimeValue); //表示单个已经完成
}
catch (Exception)
{ }
}
StartAsyncCore();
}
}

谢谢浏览!

一个简单的利用 WebClient 异步下载的示例(五)(完结篇)的更多相关文章

  1. 一个简单的利用 WebClient 异步下载的示例(三)

    继续上一篇 一个简单的利用 WebClient 异步下载的示例(二) 后,继续优化它. 1. 直接贴代码了: DownloadEntry: public class DownloadEntry { p ...

  2. 一个简单的利用 WebClient 异步下载的示例(二)

    继上一篇 一个简单的利用 WebClient 异步下载的示例(一) 后,我想把核心的处理提取出来,成 SkyWebClient,如下: 1. SkyWebClient 该构造函数中 downloadC ...

  3. 一个简单的利用 WebClient 异步下载的示例(一)

    继上一篇文章 一个简单的利用 HttpClient 异步下载的示例 ,我们知道不管是 HttpClient,还算 WebClient,都不建议每次调用都 new HttpClient,或 new We ...

  4. 一个简单的利用 WebClient 异步下载的示例(四)

    接上一篇,我们继续优化它. 1. DownloadEntry 类 public class DownloadEntry { public string Url { get; set; } public ...

  5. 一个简单的利用 HttpClient 异步下载的示例

    可能你还会喜欢 一个简单的利用 WebClient 异步下载的示例  ,且代码更加新. 1. 定义自己的 HttpClient 类. using System; using System.Collec ...

  6. VC6下OpenGL 开发环境的构建外加一个简单的二维网络棋盘绘制示例

    一.安装GLUT 工具包 GLUT 不是OpenGL 所必须的,但它会给我们的学习带来一定的方便,推荐安装. Windows 环境下的GLUT 本地下载地址:glut-install.zip(大小约为 ...

  7. 一个简单的AXIS远程调用Web Service示例

    我们通常都将编写好的Web Service发布在Tomcat或者其他应用服务器上,然后通过浏览器调用该Web Service,返回规范的XML文件.但是如果我们不通过浏览器调用,而是通过客户端程序调用 ...

  8. Java RMI 实现一个简单的GFS(谷歌文件系统)——背景与设计篇

    目录 背景 系统设计 1. 系统功能 2. Master组件 2.1 命名空间 2.2 心跳机制 2.3 故障恢复和容错机制 3. ChunkServer组件 3.1 本地存储 3.2 内存命中机制 ...

  9. 使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(三)(错误整理篇)

    使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(一) 使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(二) 以上两篇已经把流 ...

随机推荐

  1. 基于 H5 + WebGL 实现的地铁站 3D 可视化系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  2. json字符串转为php数组,使用随机字符串生成订单号(学生笔记)

    //提交订单 function add_order(){ session_start(); // var_dump($_SESSION); // die(); // session_destroy() ...

  3. dependencies和devDependencies区别

    vue-cli3.x项目的package.json中,有两种依赖: dependencies:项目依赖.在编码阶段和呈现页面阶段都需要的,也就是说,项目依赖即在开发环境中,又在生产环境中.如js框架v ...

  4. Java性能之synchronized锁的优化

    synchronized / Lock 1.JDK 1.5之前,Java通过synchronized关键字来实现锁功能 synchronized是JVM实现的内置锁,锁的获取和释放都是由JVM隐式实现 ...

  5. 实验吧简单的SQL注入1,简单的SQL注入

    接上面一篇博客. 实验吧简单的sql注入1 题目连接   http://ctf5.shiyanbar.com/423/web/ 同样,直接输入 1加个但引号,结果下面有返回错误,            ...

  6. SQL学习_WHERE 数据过滤

    1.比较运算符 SQL:SELECT name, hp_max FROM heros WHERE hp_max > 6000 SQL:SELECT name, hp_max FROM heros ...

  7. 《Python自动化测试九章经》

    Python是当前非常流行的一门编程语言,它除了在人工智能.数据处理.Web开发.网络爬虫等领域得到广泛使用之外,他也非常适合软件测试人员使用,但是,对于刚入行的测试小白来说,并不知道学习Python ...

  8. 生成前N个自然数随机置换的3个程序

    问题描述: 假设需要生成前N个自然数的一个随机置换.例如,{4,3,1,5,2}和{3,1,4,2,5}就是合法的置换,但{5,4,1,2,1}却不是,因为数1出现两次而数3却没有.这个程序常常用于模 ...

  9. javascript 生成指定范围内的随机数

    js 生成任意2个区间内的随机整数,js 生成两个数之间的随机数 function random(m,n) { return Math.floor(Math.random() * (n - m)) + ...

  10. 1.Python网络编程_UDP(简略版)

    # -*- coding: utf-8 -*- #2019-11-24 import socket def recv(): udp_socket=socket.socket(socket.AF_INE ...