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

使用GraphServiceClient调用MS Graph API手动缓冲请求出现重复ID问题

问题根因
  • 核心原因是C#的循环变量闭包捕获陷阱:for循环中声明的迭代变量i在整个循环周期内是同一个引用实例,你传入Task.Run的匿名异步函数捕获的是变量i本身,而非每次循环时i的瞬时值。你之前的串行实现是每次循环都await任务完成才会进入下一次迭代,i的取值和任务执行时机完全同步,所以不会出现问题;但修改为并行后,提交完的任务不会立即执行,等任务真正开始执行读取usersAADList[i]时,循环已经完成了多次i的自增,所有未执行的任务拿到的都是当前i的最新值,就会出现批量重复请求同一个用户、输出重复ID的现象。
  • 额外问题:FillOneUserAuthMethods本身就是返回Task的异步方法,不需要额外套一层Task.Run,属于无意义的线程池调度开销。
修复代码

直接在循环体内创建局部变量存储当前迭代的用户对象,让lambda捕获该局部变量(每次循环迭代的局部变量都是独立实例,不会被后续循环修改),同时移除冗余的Task.Run即可:

private static async Task FillUsersAuthMethods(GraphServiceClient graphClient, List<POCO.userAAD> usersAADList)
{
    List<Task<string>> bufferTasksList = new List<Task<string>>();
    const int maxParallel = 4;

    for (int i = 0; i < usersAADList.Count; ++i)
    {
        // 关键:循环内部定义局部变量,存储当前迭代的用户对象,lambda捕获该独立局部变量
        var currentUser = usersAADList[i];
        bufferTasksList.Add(FillOneUserAuthMethods(graphClient, currentUser));

        if (bufferTasksList.Count >= maxParallel || i + 1 == usersAADList.Count)
        {
            await Task.WhenAll(bufferTasksList);
            bufferTasksList.Clear();
        }
    }
}
可选优化方案

如果需要更灵活的并发度控制,也可以用SemaphoreSlim实现,代码更简洁不易出错:

private static async Task FillUsersAuthMethods(GraphServiceClient graphClient, List<POCO.userAAD> usersAADList)
{
    using var semaphore = new SemaphoreSlim(4); // 最多4个并发
    var tasks = usersAADList.Select(async user =>
    {
        await semaphore.WaitAsync();
        try
        {
            await FillOneUserAuthMethods(graphClient, user);
        }
        finally
        {
            semaphore.Release();
        }
    });
    await Task.WhenAll(tasks);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 03:48:02