继续上一篇 一个简单的利用 WebClient 异步下载的示例(二) 后,继续优化它。
1. 直接贴代码了:
DownloadEntry:
public class DownloadEntry { public string Url { get; set; } public string Path { get; set; } /// <summary> /// 当前处理的数据 /// </summary> public object Data { get; set; } public DownloadEntry(string url, string savedPath) : this(url, savedPath, null) { } public DownloadEntry(string url, string savedPath, object data) { Url = url; Path = savedPath; Data = data; } }
SkyParallelWebClient 类:
/// <summary> /// 并行的 WebClient /// </summary> public class SkyParallelWebClient { ConcurrentQueue<DownloadEntry> OptionDataList = new ConcurrentQueue<DownloadEntry>(); public SkyParallelWebClient(ICollection<DownloadEntry> downloadConfigs) { if (downloadConfigs == null) { throw new ArgumentNullException("downloadConfigs"); } foreach (var item in downloadConfigs) { OptionDataList.Enqueue(item); } } public bool TryStart(int parallelCount) { string errorMessage; return TryStart(parallelCount, out errorMessage); } public bool TryStart(int parallelCount, out string errorMessage) { try { StartCore(parallelCount); errorMessage = null; return true; } catch (Exception ex) { errorMessage = ex.Message; return false; } } protected void StartCore(int takeCount) { if (OptionDataList.Count == 0) { return; } IList<Task> processingTasks = new List<Task>(); for (int i = 0; i < takeCount; i++) { if (OptionDataList.Count == 0) { break; } DownloadEntry downloadEntry; if (!OptionDataList.TryDequeue(out downloadEntry)) { break; } var task = Task.Factory.StartNew((data) => { DownloadEntry temp = data as DownloadEntry; if (downloadEntry == null) return; DownloadFile(temp); }, downloadEntry); processingTasks.Add(task); } Task.WaitAll(processingTasks.ToArray()); if (OptionDataList.Count > 0) { StartCore(takeCount); } } private static void DownloadFile(DownloadEntry downloadEntry) { using (WebClient webClient = new WebClient()) { //set this to null if there is no proxy webClient.Proxy = null; webClient.DownloadFile(new Uri(downloadEntry.Url), downloadEntry.Path); } } }
客户端测试代码:
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)); } ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncRunSkyParallelWebClient), downloadConfigs); } private void AsyncRunSkyParallelWebClient(object state) { SkyParallelWebClient skyParallelWebClient = new SkyParallelWebClient((List<DownloadEntry>)state); string errorMessage; if (!skyParallelWebClient.TryStart(20, out errorMessage)) { MessageBox.Show(errorMessage); return; } WhenAllDownloaded(); }
2. 效果图
由于并行后,无法统计下载进度,所以没有进度条了。注意:每一项完成后,没有日志,也就是说下面的 “编号 99 下载 True”是不会有的!
3. 总结
以上测试可以解决并行下载的任务,且体验相对来说比较好。但有一个不完美的地方,就是该任务以一定量(比如:20个)任务为一个单元,只要这个单元中有一个任务耗费了太多太多时间,即使其它 19 个任务都非常快速地完成了,也要等在那里。下篇文章,我们将再次完善它。
谢谢浏览!