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

多线程调用Azure Function写入Blob递增序号的线程安全实现咨询

实现线程安全计数的核心方案

核心解决多并发下的读写竞态问题,直接使用「读Blob取值+1写回」的逻辑会出现并发覆盖导致计数重复,这里采用Azure Blob原生的乐观并发控制(ETag校验) 实现原子更新,无需引入额外的中间件即可保证计数严格递增不重复。


示例代码(C# Azure Function)

首先需要安装NuGet包:Azure.Storage.Blobs

using Azure;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Threading.Tasks;

public static class SequenceIncrementFunction
{
    // Blob存储连接字符串,建议存在Function的应用配置中,不要硬编码
    private const string BlobConnectionString = "YOUR_BLOB_CONNECTION_STRING";
    private const string ContainerName = "sequence-container";
    private const string BlobName = "current-sequence.txt";

    [FunctionName("IncrementSequence")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        var blobServiceClient = new BlobServiceClient(BlobConnectionString);
        var containerClient = blobServiceClient.GetBlobContainerClient(ContainerName);
        await containerClient.CreateIfNotExistsAsync();
        var blobClient = containerClient.GetBlobClient(BlobName);

        int newSequence = 0;
        // 循环重试直到写入成功
        while (true)
        {
            try
            {
                int currentSequence;
                ETag currentEtag;

                // 情况1:Blob已经存在,读取当前值和ETag
                if (await blobClient.ExistsAsync())
                {
                    var downloadResponse = await blobClient.DownloadAsync();
                    using var streamReader = new StreamReader(downloadResponse.Value.Content);
                    var content = await streamReader.ReadToEndAsync();
                    currentSequence = int.TryParse(content, out var num) ? num : 0;
                    currentEtag = downloadResponse.Value.Details.ETag;
                    
                    newSequence = currentSequence + 1;
                }
                // 情况2:Blob不存在,首次调用初始值为1
                else
                {
                    newSequence = 1;
                    // 首次写入要求Blob不存在才允许写入,避免并发创建冲突
                    await blobClient.UploadAsync(BinaryData.FromString(newSequence.ToString()), 
                        new BlobUploadOptions { Conditions = new BlobRequestConditions { IfNoneMatch = ETag.All } });
                    break;
                }

                // 携带ETag做写入校验,只有当前Blob的ETag和读取到的一致时才允许写入
                await blobClient.UploadAsync(BinaryData.FromString(newSequence.ToString()), 
                    new BlobUploadOptions { Conditions = new BlobRequestConditions { IfMatch = currentEtag } },
                    overwrite: true);
                break;
            }
            catch (RequestFailedException ex) when (ex.Status == 412)
            {
                // 412错误代表ETag不匹配,有其他请求已经更新了Blob,重试即可
                log.LogInformation("并发冲突,重试更新计数");
            }
        }

        return new OkObjectResult(new { SequenceNumber = newSequence });
    }
}

方案说明
  • 所有并发控制逻辑都在Function侧实现,控制台多线程调用端无需做额外处理,直接正常发起HTTP请求即可
  • 每次写入前都会校验ETag,只有当前Blob没有被其他请求修改的情况下写入才会成功,保证不会出现并发覆盖导致的计数重复
  • 冲突时自动重试,不会出现漏计数、跳号的问题,最终Blob中的数值等于总调用次数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 10:36:06