如何在C#的Task.WhenAll方法中统计每个HTTP请求的耗时
实现每个HTTP请求的耗时统计方案
首先要修正原代码的一个关键问题:每次调用GetRequest都新建HttpClient会耗尽套接字资源,因为HttpClient的连接池与实例绑定,频繁创建会造成资源浪费甚至触发异常,必须复用HttpClient实例。
接下来是耗时统计的具体实现,核心思路是为每个请求单独记录起始/结束时间,同时关联请求标识(比如你的循环索引),方便对应到具体请求:
1. 定义结果存储类型(可选但推荐)
用来统一保存请求标识、耗时和响应结果:
public class RequestTimingResult { public int RequestIndex { get; set; } public TimeSpan ElapsedTime { get; set; } public HttpResponseMessage Response { get; set; } }
2. 修改请求执行与统计逻辑
public static async Task Start() { // 复用同一个HttpClient实例 using var httpClient = new HttpClient(); var tasks = new List<Task<RequestTimingResult>>(); for (int i = 0; i < 10; i++) { // 捕获循环变量,避免闭包引用问题 int requestIndex = i; var task = Task.Run(async () => { // 启动计时器 var stopwatch = Stopwatch.StartNew(); // 执行请求 var response = await httpClient.GetAsync($"https://address.com?p={requestIndex}"); // 停止计时并计算耗时 stopwatch.Stop(); return new RequestTimingResult { RequestIndex = requestIndex, ElapsedTime = stopwatch.Elapsed, Response = response }; }); tasks.Add(task); } // 等待所有请求完成 var allResults = await Task.WhenAll(tasks); // 输出每个请求的耗时统计 foreach (var result in allResults) { Console.WriteLine($"请求 {result.RequestIndex}:耗时 {result.ElapsedTime.TotalMilliseconds:F2} 毫秒,响应状态码 {result.Response.StatusCode}"); } }
关键细节说明
- 复用
HttpClient:用using包裹确保资源正确释放,同时整个循环复用同一个实例,避免套接字耗尽问题。 - 闭包问题处理:循环内创建
requestIndex变量捕获当前循环值,否则所有任务会引用同一个循环变量,导致最终拿到的都是最后一个索引值。 - 精确计时:使用
Stopwatch类计时,精度远高于DateTime,适合统计短时间操作的耗时。
内容的提问来源于stack exchange,提问作者barteloma
相关产品推荐
相关产品推荐

