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

.NET Framework下C#批量异步调用外部API的最优方法(避限流)

.NET Framework中批量异步调用限流API的最优实现

针对你的需求,分批异步执行+固定间隔等待是最直接且可控的方案,你的初始思路方向是对的,我们可以把它封装成通用的工具方法,让代码更整洁、复用性更强,同时确保严格遵守限流规则。

核心实现思路

  1. 将所有API请求按指定批次大小拆分
  2. 异步执行每一批的所有请求(用Task.WhenAll)
  3. 等待当前批次完成且等待指定时长(取两者中耗时更长的那个),再发起下一批,避免触发限流

通用分批异步处理方法

下面是针对.NET Framework的实现,全程异步非阻塞,不依赖任何第三方库:

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public static class BatchProcessor
{
    /// <summary>
    /// 分批异步处理任务,每批完成后等待指定时长再执行下一批
    /// </summary>
    /// <typeparam name="T">任务项类型</typeparam>
    /// <param name="items">待处理的所有项</param>
    /// <param name="batchSize">每批处理的数量</param>
    /// <param name="processItemAsync">处理单个项的异步方法</param>
    /// <param name="delayBetweenBatchesMs">批与批之间的等待时长(毫秒)</param>
    /// <param name="cancellationToken">取消令牌,用于终止整个批量操作</param>
    public static async Task ProcessInBatchesAsync<T>(
        IEnumerable<T> items,
        int batchSize,
        Func<T, CancellationToken, Task> processItemAsync,
        int delayBetweenBatchesMs,
        CancellationToken cancellationToken = default)
    {
        var itemList = items.ToList();
        for (int i = 0; i < itemList.Count; i += batchSize)
        {
            // 检查是否已取消
            cancellationToken.ThrowIfCancellationRequested();

            // 获取当前批次的项
            var currentBatch = itemList.Skip(i).Take(batchSize);

            // 启动当前批次的所有异步任务
            var batchTasks = currentBatch.Select(item => processItemAsync(item, cancellationToken)).ToList();

            // 等待当前批次完成,同时确保至少等待指定时长(哪个耗时久等哪个)
            await Task.WhenAll(Task.WhenAll(batchTasks), Task.Delay(delayBetweenBatchesMs, cancellationToken));
        }
    }
}

使用示例

以你提到的100次API调用、每批20次、间隔5秒为例:

// 模拟外部API调用方法
private async Task CallExternalApiAsync(int requestId, CancellationToken cancellationToken)
{
    // 实际调用逻辑替换为你的API请求代码
    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 发起请求 {requestId}");
    await Task.Delay(150, cancellationToken); // 模拟API响应耗时
    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 请求 {requestId} 完成");
}

// 执行批量调用
public async Task RunBatchCalls()
{
    var allRequests = Enumerable.Range(1, 100);
    var cancellationTokenSource = new CancellationTokenSource();

    try
    {
        await BatchProcessor.ProcessInBatchesAsync(
            items: allRequests,
            batchSize: 20,
            processItemAsync: CallExternalApiAsync,
            delayBetweenBatchesMs: 5000,
            cancellationToken: cancellationTokenSource.Token);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("批量操作已取消");
    }
}

方案优势

  • 严格限流:确保每批请求仅在上一批完成且等待足够时长后才发起,完全规避触发API限流的风险
  • 异步高效:全程使用异步操作,不会阻塞主线程,比同步的Parallel.ForEach性能更优
  • 通用复用:封装的工具方法可用于任何需要分批异步处理的场景,不止API调用
  • 可取消性:支持CancellationToken,方便在需要时终止整个批量操作

额外优化建议

如果API的限流规则是基于时间窗口(例如每分钟最多100次),而非固定批次,你可以调整逻辑:记录每批的开始时间,计算下一批的最早启动时间,确保在时间窗口内的请求总数不超过限制。不过针对你当前的需求,上述方案已经完全足够。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:25:26