为何Task会被取消?多线程API压测代码异常排查
问题原因及修复方案
核心错误点
同步阻塞异步操作引发死锁/任务取消
CallApiAndGetStatusCode中使用httpClient.SendAsync(request).Result同步阻塞异步方法,在存在同步上下文的环境(如ASP.NET、UI程序)中,会导致异步回调无法回到原上下文,最终触发任务取消(Task Canceled)异常。错误的并发实现
原代码通过await Task.Run()在for循环中串行执行每个线程任务,完全没有实现多线程并发的压力测试效果——每个Task.Run必须等待上一个执行完成才会启动,无法达到压测目的。潜在的线程安全问题
虽然当前是串行执行,但如果后续改为并发,List<int>并非线程安全集合,多线程同时调用AddRange会导致数据损坏或异常。
修复后的代码
第一步:修正API调用方法为异步非阻塞
private async Task<int> CallApiAndGetStatusCodeAsync() { try { var request = new HttpRequestMessage(HttpMethod.Get, "some url"); var responseResult = await httpClient.SendAsync(request); return (int)responseResult.StatusCode; } catch (Exception e) { logger.LogError(e, "Calling API failed"); return -1; // 返回错误标识,避免编译报错 } }
第二步:实现真正的并发压力测试并安全聚合结果
private async Task DoWork() { // 并行创建10个任务,每个任务内部串行执行50次API调用 var concurrentTasks = Enumerable.Range(0, 10).Select(async _ => { var taskResponses = new List<int>(); for (int i = 0; i < 50; i++) { taskResponses.Add(await CallApiAndGetStatusCodeAsync()); } return taskResponses; }); // 等待所有并发任务完成,统一聚合结果 var allResponseGroups = await Task.WhenAll(concurrentTasks); var allResponses = allResponseGroups.SelectMany(group => group).ToList(); // 后续处理逻辑 }
额外优化建议
- 调整连接数限制:默认HttpClient对单个域名的并发连接数限制为6,压测时可通过
ServicePointManager.DefaultConnectionLimit = 100;(或更高值)调整,避免连接耗尽导致的请求失败。 - 使用IHttpClientFactory:如果是.NET Core/.NET 5+项目,建议通过依赖注入使用
IHttpClientFactory创建HttpClient,避免手动管理客户端实例带来的连接泄漏问题。
内容的提问来源于stack exchange,提问作者havij
相关产品推荐
相关产品推荐

