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

异步批量上传Azure Blobs出现MD5不匹配错误,求排查原因

异步上传Azure Blob出现MD5不匹配的问题分析及修复

问题1:SemaphoreSlim使用错误,未正确控制并发数

你的异步代码中,await semaphore.WaitAsync();被放在foreach循环之外,仅获取了一次信号量许可,后续创建的所有任务会同时执行,完全没达到限制50并发的目的。当batchSize远大于50时,会瞬间发起大量并发请求,进而引发:

  • 网络资源竞争,部分请求的内容传输出现损坏
  • Azure存储服务因并发过高,处理请求时出现MD5计算偏差
  • 连接池耗尽,请求复用连接时出现数据混淆

正确用法:将semaphore.WaitAsync();移到每个任务内部,确保每个任务执行前先获取许可,完成后释放:

var semaphore = new SemaphoreSlim(50);
var tasks = new List<Task>();
foreach (var callObj in source.Skip(curBatch * batchSize).Take(batchSize))
{
    // 显式捕获当前循环变量,避免闭包共享问题
    var currentCallObj = callObj;
    tasks.Add(Task.Run(async () =>
    {
        await semaphore.WaitAsync();
        try
        {
            var name = ...; // 基于currentCallObj生成Blob名称
            var blob = blobContainerForCalls.GetBlockBlobReference(name);
            var data = ...; // 基于currentCallObj生成待上传数据
            await blob.UploadTextAsync(JsonConvert.SerializeObject(data));
        }
        finally
        {
            semaphore.Release();
        }
    }));
}
await Task.WhenAll(tasks);

问题2:闭包变量捕获的潜在风险

如果项目使用C#5之前的版本,foreach循环的callObj变量是在循环外部声明的,所有任务的闭包会捕获同一个callObj实例,最终所有任务都会使用循环最后一次的callObj数据。这会导致:

  • 多个任务上传相同内容到不同Blob路径
  • 序列化的data与目标Blob不匹配,服务器计算的MD5自然和请求中的不一致

即使是C#5及以后版本(foreach变量每次循环重新声明),显式捕获当前变量也能避免潜在陷阱。

问题3:共享blob变量引发的资源竞争

从代码结构看,blob是方法级别的共享变量,多个异步任务会同时修改它,导致不同任务的Blob引用被覆盖。这会造成:

  • 不同任务的上传内容被错误指向同一Blob路径
  • 内容传输混乱,服务器接收的内容与请求MD5不匹配

修复:将blob变量声明在任务内部,确保每个任务拥有独立的Blob引用,消除竞争。

补充说明

同步代码正常的原因是它逐个处理请求,没有并发带来的资源竞争和变量共享问题,也不会因并发过高导致传输异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:03:28