You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Task.WhenAll并行化.NET中的Http请求任务

并行处理批量Http请求的实现方案

一、核心实现:用Task.WhenAll收集结果再关联

不用在异步任务里操作ConcurrentDictionary,而是让每个任务返回item与对应结果的元组,等所有任务完成后一次性构建字典,既避免并发写入的锁开销,又能精准关联结果:

// 1. 获取初始items列表
var items = await FetchItemsViaHttpAsync();

// 2. 为每个item创建异步任务,返回(item, 处理结果)的元组
var processingTasks = items.Select(async item =>
{
    var itemResult = await FetchItemDetailViaHttpAsync(item);
    return (Item: item, Result: itemResult);
});

// 3. 等待所有任务完成,拿到所有结果
var allResults = await Task.WhenAll(processingTasks);

// 4. 直接将结果转成ConcurrentDictionary,初始化时指定预期长度
var resultDictionary = new ConcurrentDictionary<ItemType, ResultType>(
    allResults.ToDictionary(kv => kv.Item, kv => kv.Result)
);

二、ConcurrentDictionary初始化优化

初始化时的预期长度直接用items的总数,因为最终字典里的键值对数量和items数量完全一致,和并行任务的线程数无关。线程数由.NET的任务调度器自动管理,无需手动指定。如果需要手动控制并发请求数(比如规避API限流),可以用SemaphoreSlim:

// 限制同时最多10个请求
var requestSemaphore = new SemaphoreSlim(10);

var processingTasks = items.Select(async item =>
{
    await requestSemaphore.WaitAsync();
    try
    {
        var itemResult = await FetchItemDetailViaHttpAsync(item);
        return (Item: item, Result: itemResult);
    }
    finally
    {
        requestSemaphore.Release();
    }
});

如果一定要指定ConcurrentDictionary的并发级别(影响内部锁的数量),可以传入Environment.ProcessorCount(和CPU核心数匹配),结合预期长度的完整初始化代码:

var resultDictionary = new ConcurrentDictionary<ItemType, ResultType>(
    concurrencyLevel: Environment.ProcessorCount,
    capacity: items.Count
);

三、关键注意点

  • 避免在异步任务内直接写入ConcurrentDictionary:虽然线程安全,但会带来额外的锁竞争开销,不如先收集所有结果再批量构建高效。
  • 并行度不是越高越好:如果目标API有限流策略,必须用信号量控制并发数,否则容易触发429等限流错误。

内容的提问来源于stack exchange,提问作者Zenith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 14:10:06