当前位置: 首页 > article >正文

C#异步编程实战:用Task.WhenAll和CancellationTokenSource打造一个高并发、可取消的批量文件下载器

C#异步编程实战构建高并发可取消的批量文件下载器在当今互联网应用中处理大量文件下载是常见需求。无论是电商平台的商品图片抓取、企业文档管理系统还是数据备份工具都需要高效可靠的批量下载能力。传统同步下载方式不仅速度慢还无法充分利用现代多核CPU的优势。本文将带你用C#的异步编程模型构建一个支持并发控制、进度报告和随时取消的专业级下载工具。1. 异步编程基础与核心组件异步编程的核心目标是让应用程序在等待I/O操作如网络请求、文件读写时不阻塞主线程。C#通过async/await语法糖简化了异步代码的编写底层依赖以下几个关键组件Task表示异步操作可携带返回值或仅作为状态标识CancellationTokenSource提供取消异步操作的机制IProgress实现跨线程的进度报告HttpClient现代.NET中执行HTTP请求的首选方式// 基础异步方法示例 public async Taskstring DownloadStringAsync(string url) { using var httpClient new HttpClient(); return await httpClient.GetStringAsync(url); }Task与Thread的核心区别特性TaskThread资源管理基于线程池自动复用每次新建独立线程取消支持原生支持需手动实现异常处理聚合异常捕获独立异常处理延续任务ContinueWith/await需回调或轮询2. 构建下载器核心架构2.1 设计下载任务模型每个下载任务应包含以下信息文件URL本地保存路径下载状态等待中、下载中、已完成、已取消已下载字节数异常信息如有public class DownloadItem { public string Url { get; set; } public string SavePath { get; set; } public DownloadStatus Status { get; set; } public long BytesDownloaded { get; set; } public Exception Error { get; set; } } public enum DownloadStatus { Pending, Downloading, Completed, Canceled, Failed }2.2 实现并发下载控制器核心思路是使用Task.WhenAll管理多个并发下载任务配合SemaphoreSlim控制最大并发数public class BatchDownloader { private readonly HttpClient _httpClient; private readonly SemaphoreSlim _semaphore; private CancellationTokenSource _cts; public BatchDownloader(int maxConcurrency) { _httpClient new HttpClient(); _semaphore new SemaphoreSlim(maxConcurrency); } public async Task DownloadAllAsync( IEnumerableDownloadItem items, IProgressDownloadProgress progress, CancellationToken cancellationToken) { _cts CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var tasks items.Select(item DownloadFileAsync(item, progress, _cts.Token)); await Task.WhenAll(tasks); } private async Task DownloadFileAsync( DownloadItem item, IProgressDownloadProgress progress, CancellationToken token) { await _semaphore.WaitAsync(token); try { item.Status DownloadStatus.Downloading; // 实际下载逻辑... } finally { _semaphore.Release(); } } }3. 高级功能实现3.1 可取消下载实现通过CancellationToken实现优雅取消private async Task DownloadFileAsync( DownloadItem item, IProgressDownloadProgress progress, CancellationToken token) { try { using var response await _httpClient.GetAsync( item.Url, HttpCompletionOption.ResponseHeadersRead, token); response.EnsureSuccessStatusCode(); await using var stream await response.Content.ReadAsStreamAsync(token); await using var fileStream new FileStream( item.SavePath, FileMode.Create, FileAccess.Write); var buffer new byte[8192]; int bytesRead; while ((bytesRead await stream.ReadAsync(buffer, token)) 0) { await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), token); item.BytesDownloaded bytesRead; progress?.Report(new DownloadProgress(item)); } item.Status DownloadStatus.Completed; } catch (OperationCanceledException) { item.Status DownloadStatus.Canceled; File.Delete(item.SavePath); } catch (Exception ex) { item.Status DownloadStatus.Failed; item.Error ex; } }3.2 进度报告机制定义进度报告模型和使用方式public class DownloadProgress { public DownloadItem Item { get; } public int TotalItems { get; } public int CompletedItems { get; } public DownloadProgress(DownloadItem item, int total, int completed) { Item item; TotalItems total; CompletedItems completed; } } // 在UI层的使用示例 var progress new ProgressDownloadProgress(p { progressBar.Value p.CompletedItems * 100 / p.TotalItems; label.Text ${p.Item.Url} - {p.Item.BytesDownloaded / 1024}KB; });4. 异常处理与性能优化4.1 聚合异常处理当使用Task.WhenAll时多个任务的异常会被包装在AggregateException中try { await Task.WhenAll(tasks); } catch (AggregateException ae) { foreach (var ex in ae.InnerExceptions) { logger.LogError($下载失败: {ex.Message}); } }4.2 性能优化技巧缓冲区大小调整根据网络状况动态调整缓冲区大小连接复用保持HttpClient单例而非每次新建并行度控制根据CPU核心数和带宽合理设置并发数断点续传支持Range请求实现断点续传// 断点续传实现示例 var request new HttpRequestMessage(HttpMethod.Get, item.Url); if (File.Exists(item.SavePath)) { var fileInfo new FileInfo(item.SavePath); request.Headers.Range new RangeHeaderValue(fileInfo.Length, null); await using var fileStream new FileStream( item.SavePath, FileMode.Append, FileAccess.Write); // ... }5. 完整实现与使用示例5.1 完整BatchDownloader类public class BatchDownloader : IDisposable { private readonly HttpClient _httpClient; private readonly SemaphoreSlim _semaphore; private bool _disposed; public BatchDownloader(int maxConcurrency 4) { _httpClient new HttpClient { Timeout TimeSpan.FromMinutes(10) }; _semaphore new SemaphoreSlim(maxConcurrency); } public async Task DownloadAllAsync( ICollectionDownloadItem items, IProgressDownloadProgress progress null, CancellationToken cancellationToken default) { var cts CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var tasks new ListTask(); var completedCount 0; foreach (var item in items) { tasks.Add(DownloadFileAsync(item, progress, cts.Token) .ContinueWith(t { Interlocked.Increment(ref completedCount); progress?.Report(new DownloadProgress(item, items.Count, completedCount)); }, TaskScheduler.Default)); } await Task.WhenAll(tasks); } // ...其他方法实现... public void Dispose() { if (_disposed) return; _httpClient.Dispose(); _semaphore.Dispose(); _disposed true; } }5.2 控制台应用示例static async Task Main(string[] args) { var urls new[] { https://example.com/file1.zip, https://example.com/file2.pdf, // ...更多URL }; var items urls.Select((url, i) new DownloadItem { Url url, SavePath Path.Combine(Downloads, $file_{i}{Path.GetExtension(url)}) }).ToList(); Directory.CreateDirectory(Downloads); var progress new ProgressDownloadProgress(p { Console.WriteLine(${p.Item.Url} - {p.Item.BytesDownloaded / 1024}KB); }); using var downloader new BatchDownloader(maxConcurrency: 4); using var cts new CancellationTokenSource(); Console.CancelKeyPress (s, e) { e.Cancel true; cts.Cancel(); Console.WriteLine(取消请求已发送...); }; try { await downloader.DownloadAllAsync(items, progress, cts.Token); Console.WriteLine(所有下载任务完成); } catch (OperationCanceledException) { Console.WriteLine(下载已取消); } catch (Exception ex) { Console.WriteLine($发生错误: {ex.Message}); } }在实际项目中这个下载器可以轻松集成到WPF、WinForms或ASP.NET Core应用中。我在一个电商数据采集项目中使用了类似实现将数千个商品图片的下载时间从原来的30分钟缩短到不到2分钟。关键点在于合理设置并发数通常4-8个为宜和缓冲区大小8-32KB效果最佳。

相关文章:

C#异步编程实战:用Task.WhenAll和CancellationTokenSource打造一个高并发、可取消的批量文件下载器

C#异步编程实战:构建高并发可取消的批量文件下载器 在当今互联网应用中,处理大量文件下载是常见需求。无论是电商平台的商品图片抓取、企业文档管理系统,还是数据备份工具,都需要高效可靠的批量下载能力。传统同步下载方式不仅速度…...

构建企业级服务中台的几个关键设计思想

作为一名技术人员,我理解的“服务”,不仅仅是一个代码功能,更是一套复杂的业务流程和数据处理系统。在接触了“帮我吧”这样的企业级一体化智能服务管理平台后,我发现它背后蕴含的设计思想,非常值得我们技术人借鉴。 …...

告别复杂配置!ANIMATEDIFF PRO保姆级部署教程,RTX 4090开箱即用

告别复杂配置!ANIMATEDIFF PRO保姆级部署教程,RTX 4090开箱即用 1. 为什么选择ANIMATEDIFF PRO 如果你正在寻找一款能够生成电影级视频的AI工具,ANIMATEDIFF PRO可能是目前最强大的选择之一。它基于AnimateDiff架构和Realistic Vision V5.1…...

STM32F401RE HSI+PLL 84MHz轻量时钟配置库

1. 项目概述ST_401_84MHZ是一个面向 STM32F401RE Nucleo 开发板的轻量级时钟配置库,其核心目标是将系统主频(SYSCLK)稳定、可靠地提升至84 MHz。该频率并非芯片默认出厂配置(F401RE 的默认 HSI 为 16 MHz,复位后 SYSCL…...

OpenCL维度跨越

上面讲到怎么在一维问题里面进行操作,那么下面来讲述一下二维(矩阵或者图像)。在OpenCL里面,这种跨越核心在于坐标系的变化。坐标系的升级:在一维里,我们只用到了get_global_id(0)。但是在二维中&#xff0…...

告别复杂配置:M2FP人体解析镜像一键部署,小白也能轻松上手

告别复杂配置:M2FP人体解析镜像一键部署,小白也能轻松上手 1. 为什么你需要M2FP人体解析服务 想象一下,你正在开发一个虚拟试衣应用,或者需要分析监控视频中的人物行为。传统方法需要手动标注每一帧图像中的人体部位&#xff0c…...

告别黑盒:手把手教你定制Unity WebGL的加载页面与浏览器交互(模板、JS插件、通信全解析)

深度定制Unity WebGL:从加载界面到浏览器交互的全链路实战指南 1. 为什么需要定制WebGL加载体验? 当用户首次访问基于Unity WebGL构建的网页应用时,默认的灰色进度条和纯白背景往往无法传递产品调性。数据显示,经过视觉优化的加载…...

【Django 实战】从零打造功能完备的博客系统——爱博客(iBlog)

【Django 实战】从零打造功能完备的博客系统——爱博客(iBlog) 摘要:本文详细介绍了一个基于 Django 4.2 Bootstrap 5.3 开发的全功能博客系统,包含用户管理、文章发布、双重审核、树形评论、AJAX 互动等核心功能。项目代码完整&…...

GriddyCode:用Lua脚本打造个性化代码编辑器的终极指南

GriddyCode:用Lua脚本打造个性化代码编辑器的终极指南 【免费下载链接】griddycode 项目地址: https://gitcode.com/GitHub_Trending/gr/griddycode GriddyCode是一款基于Godot引擎开发的开源代码编辑器,它通过独特的Lua脚本系统让开发者能够深度…...

【56页PPT】工业互联网工业超脑智能制造智慧工厂解决方案:总体架构设计、九大核心价值、九大数字化详细功能介绍、五大要素......

本方案以“工业超脑”为核心,构建了一个覆盖研发、生产、设备、能源、质量、安环、供应链等全流程的数字化工厂体系。通过物联网、大数据、人工智能等技术,实现数据驱动的智能决策、预测性维护、能效优化与安全保障,助力化工企业实现本质安全…...

从干系人管理到项目交付:绩效域全流程避坑指南

从干系人管理到项目交付:绩效域全流程避坑指南 在项目管理领域,干系人管理和项目交付是决定项目成败的两大核心要素。据统计,近70%的项目失败案例可追溯至干系人管理不当或交付流程失控。对于已经掌握基础项目管理方法的中级项目经理而言&…...

分布式驱动电动汽车:最优横摆力矩控制与规则扭矩分配控制的对比研究——基于LQR计算与最小附着利...

分布式驱动电动汽车 直接横摆力矩控制 最优/规则扭矩分配控制 上层lqr计算 下层最小附着利用率分配 扭矩分配 对比传统esc 效果优良 稳定性控制 操纵稳定性 matlab simulink代码源码 carsim联合仿真 深夜调车党的工位上总少不了一杯冰美式,摸着方向盘力反馈器突然想…...

48个适合人力资源工作和运营的AI提示词

本提示库包含了直接源自活动分享见解且受其启发的实用 AI 提示。每个部分都有可直接复制粘贴的示例,目的是帮助您节省时间、降低风险并简化人力资源工作流程。 这些提示适用于深度求索、通义千问等工具。请牢记:清晰度和背景信息至关重要,不同…...

第三部分:CHI事务类型与流程

第7章:读取事务全解析本章系统性地解析CHI协议中各类读取事务,从基础功能到高级优化机制,揭示其设计哲学与性能权衡。7.1 基础读取事务:ReadNoSnp、ReadOnce这两类事务是读取操作的基础,但设计目标和行为有本质区别。特…...

Nunchaku-flux-1-dev在.NET开发中的应用:API文档自动生成

Nunchaku-flux-1-dev在.NET开发中的应用:API文档自动生成 还在为写API文档头疼吗?试试让AI帮你自动生成 作为一名.NET开发者,你可能经常遇到这样的场景:项目进度紧张,代码写完了,却要花大量时间手动编写API…...

“我要验牌”很火吗?我特意写了个Shader去验...

引言 哈喽大家好,我是亿元程序员,相信大家都看或者听到过下面几句台词: “我要验牌(w yāo yān pǎi)”。 “牌没有问题”。 “给我擦皮鞋”。 如果要评选马年开年第一热梗,这几句台词估计能够遥遥领先。 为此,我特…...

学术写作助手:结合LaTeX与DAMOYOLO-S自动生成论文中的图表标注

学术写作助手:结合LaTeX与DAMOYOLO-S自动生成论文中的图表标注 写论文最头疼的事情之一是什么?对我而言,除了反复修改的引言和讨论部分,就是处理那些密密麻麻的图表了。尤其是实验部分,一张显微镜图像里可能有好几十个…...

PCB设计避坑指南:Cadence Allegro地孔设计与后期处理的5个常见错误及解决方法

PCB设计避坑指南:Cadence Allegro地孔设计与后期处理的5个常见错误及解决方法 在高速PCB设计中,地孔(Via)的处理往往是决定信号完整性和EMC性能的关键因素之一。作为Cadence Allegro用户,我们经常在地孔设计和后期处理…...

RNN-SVR混合模型用于光伏功率预测基于MATLAB实现

基于MATLAB实现RNN-SVR混合模型用于光伏功率预测,是一种结合深度学习时序建模能力与统计学习回归优势的有效方法。下面将详细介绍该混合模型的原理、实现步骤及MATLAB代码示例,帮助您快速搭建和训练模型。1. 混合模型概述 光伏功率预测本质上是时间序列回…...

TTP224四路电容触摸传感器模块原理与低功耗集成指南

1. TTP224四路电容式触摸传感器模块技术解析1.1 模块核心功能与工程定位TTP224是一种集成化四通道电容式触摸检测模块,其核心IC为TTP223B的多通道衍生版本。该模块并非简单的模拟信号采集单元,而是一个具备完整状态机管理能力的智能传感节点。在常态下&a…...

OBS录屏零基础入门:5分钟搞定显示器/窗口捕获(含常见问题解决)

OBS录屏零基础入门:5分钟搞定显示器/窗口捕获(含常见问题解决) 第一次打开OBS时,那个黑漆漆的界面是不是让你有点懵?别担心,这就像第一次拿到单反相机时的感觉——看似复杂,实则简单。作为一款开…...

C语言核心知识体系:嵌入式开发必学基础

1. C语言入门核心知识体系解析C语言自1972年诞生以来,凭借其简洁的语法、高效的执行性能和卓越的可移植性,成为系统编程、嵌入式开发和底层驱动开发的基石语言。它不仅是操作系统(如UNIX/Linux内核)、编译器、数据库等关键系统软件…...

DFT测试点插入实战:如何用Synopsys DFT Compiler提升芯片测试覆盖率(附避坑指南)

DFT测试点插入实战:如何用Synopsys DFT Compiler提升芯片测试覆盖率(附避坑指南) 在芯片设计领域,测试覆盖率直接关系到产品的良率和可靠性。想象一下,当你的设计进入量产阶段,却因为测试覆盖率不足导致大批…...

从保护到破解:Python pyd文件逆向工程全解析(含Cython编译过程)

Python pyd文件逆向工程深度实战指南 在Python生态中,pyd文件作为性能关键组件的载体,既承载着代码保护的重任,也面临着安全研究的挑战。本文将带您深入pyd文件的编译原理与逆向实践,掌握从保护到分析的全套技术方案。 1. pyd文件…...

EVA-01效果展示:Qwen2.5-VL-7B对动态GIF首帧与关键帧的语义一致性分析

EVA-01效果展示:Qwen2.5-VL-7B对动态GIF首帧与关键帧的语义一致性分析 1. 引言:当视觉大模型遇见动态世界 你有没有想过,让一个AI模型去“看”一段GIF动图,然后告诉你它看到了什么?这听起来简单,但背后其…...

RP2040硬件PWM驱动库:纳秒级精度与多通道确定性控制

1. 项目概述MBED_RP2040_PWM 是一款专为基于 RP2040 微控制器的 Arduino-mbed 平台设计的硬件级 PWM 驱动库。该库并非软件模拟或定时器中断驱动的“伪 PWM”,而是直接调用 RP2040 片上可编程 I/O(PIO)与硬件 PWM 模块(PWM Slice&…...

分布式驱动汽车稳定性控制的纯Simulink模型构建及控制器对比研究

分布式驱动汽车稳定性控制 采用纯Simulink模型搭建,包括控制策略和车辆动力学模型。 采用分层式直接横摆力矩控制,上层包括模型预测MPC,滑模控制SMC,PID控制,LQR控制 可灵活对四种控制器对比和选择。 另外下层基于轮胎…...

基于EP4CE22F17C8 FPGA的多媒体开发板硬件设计:SDRAM、WM8731音频与HR911105A网口集成方案

1. EP4CE22F17C8 FPGA开发板硬件设计概述 如果你正在寻找一款高性能的多媒体开发板设计方案,那么基于EP4CE22F17C8 FPGA的方案绝对值得考虑。这款开发板集成了SDRAM内存、WM8731音频编解码器和HR911105A网络接口,能够满足大多数多媒体应用的需求。我在实…...

5分钟搞定!用GPT-SoVITS把你的文字变成专属AI语音(Windows11+RTX显卡实测)

5分钟极速部署:用GPT-SoVITS打造你的数字声纹库(RTX显卡实战指南) 当视频创作者需要在凌晨三点补录旁白时,当外语教育博主想生成多语种发音示范时,一个能完美复刻自己声线的AI语音系统将成为内容生产的终极武器。GPT-S…...

美团面试:为什么要用分布式缓存?本地缓存呢?多级缓存一致性如何保证?

去年面美团的时候,面试官看着我的简历问:“我看你们项目里有个全局字典模块,里面那些省市代码、订单状态配置,是怎么做缓存的?” 我为了展现系统的高并发架构,立马拔高音量说:“为了保证系统的…...