接着上一篇,我们继续来优化。我们的 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. Koa 中间件的执行

    Node.js 中请求的处理 讨论 Koa 中间件前,先看原生 Node.js 中是如何创建 server 和处理请求的. node_server.js const http = require(&q ...

  2. go-面向对象编程(下)

    面向对象编程思想-抽象 抽象的介绍 我们在前面去定义一个结构体时候,实际上就是把一类事物的共有的 属性( 字段)和 行为( 方法)提取 出来,形成一个 物理模型(结构体).这种研究问题的方法称为抽象 ...

  3. 微信小程序使用weui构建搜索栏(searchbar)+导航(navbar)

    首先需要在lib目录中添加weui.wxss.searchbar和navbar结合主要解决两者的层次问题,即搜索框输入时,下方的检索结果能够覆盖住navbar.下面就开始发码啦: (1)wxml部分: ...

  4. [转]Eclipse插件开发之基础篇(6) SWT简介

    原文地址:http://www.cnblogs.com/liuzhuo/archive/2010/09/01/eclipse_plugin_1_3_1.html SWT(Standard Widget ...

  5. cannot resolve symbol 'XXX'

    原因:出现这种情况的原因是在IDEA或者myeclipse编译器中没有导入相关的jar包(jar包相当于类的集合,可以使用其中的类): 针对IDEA出现cannot resolve symbol 'X ...

  6. word保存为pdf

    word保存为pdf word保存为pdf word保存为pdf

  7. Spring熔断

    Hystrix不再维护,使用Resilience4j来代替.

  8. Pwnable-fd

    打开Ubuntu输入ssh fd@pwnable.kr -p2222,连接之后输入密码guest 之后就是ls -l看看里面的文件和权限,fd.fd.c.flag 看看fd.c的源码 #include ...

  9. 洛谷P1283 平板涂色 &&一本通1445:平板涂色

    题目描述 CE数码公司开发了一种名为自动涂色机(APM)的产品.它能用预定的颜色给一块由不同尺寸且互不覆盖的矩形构成的平板涂色. 为了涂色,APM需要使用一组刷子.每个刷子涂一种不同的颜色C.APM拿 ...

  10. MySQL实战45讲学习笔记:第四十讲

    一.本节概述 在上一篇文章中,我提到 MySQL 对自增主键锁做了优化,尽量在申请到自增 id 以后,就释放自增锁. 因此,insert 语句是一个很轻量的操作.不过,这个结论对于“普通的 inser ...