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

C# Web API POST接口单请求串行执行异常问题排查

请求排队接口串行执行异常原因分析

问题背景

我正在创建POC来学习如何实现支持请求排队的接口,目标是让每个请求串行执行。我编写了如下ValuesController代码:

public class ValuesController : ApiController
{
private static int _queuePosition = 0;
private SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);


public async void Post()
{
  var sendDate = DateTime.Now;
  _queuePosition++;
  await semaphore.WaitAsync();
  try
  {
    //ProcessStart
    var startProcess = DateTime.Now;
    System.Threading.Thread.Sleep(10000); //10秒
    Email.SendMail($"Queue entry date: {sendDate} | Process Start Date: {startProcess } | Submission date: {DateTime.Now} | Queued requests: {_queuePosition}");
    semaphore.Release();
  }
  finally
  {
    _queuePosition--;
  }
}

当间隔2秒发送两个请求时,收到如下日志邮件(日期格式:MM/dd/yyyy HH:mm:ss):

  • Queue entry date: 02/01/2023 16:19:55 | Process Start Date: 02/01/2023 16:19:55 | Submission date: 02/01/2023 16:20:05 | Queued requests: 2
  • Queue entry date: 02/01/2023 16:19:57 | Process Start Date: 02/01/2023 16:19:57 | Submission date: 01/02/2023 16:20:07 | Queued requests: 2

按预期第二个请求的处理应在第一个请求结束后启动,但实际并非如此,请问原因是什么?


核心问题原因

1. 信号量未实现全局共享

SemaphoreSlim是控制器的实例字段,而ASP.NET(非Core)环境中每个请求会创建一个新的ValuesController实例。这意味着两个请求各自持有独立的信号量,完全无法起到互斥串行的作用,自然可以同时执行。

2. async void引发异步生命周期管理问题

Post方法使用async void返回类型,这在ASP.NET中会导致框架无法跟踪异步操作的完成状态,可能引发上下文混乱,进一步加剧并发执行的问题。正确的异步API方法应该返回async Task。

3. 队列位置计数存在线程安全问题

_queuePosition++和_queuePosition--不是原子操作,在并发场景下会出现竞态条件,导致日志中显示的Queued requests数值不准确。


修正后的代码示例

public class ValuesController : ApiController
{
    private static int _queuePosition = 0;
    // 改为静态成员,确保所有请求共享同一个信号量
    private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

    // 使用async Task替代async void,让框架正确跟踪异步生命周期
    public async Task Post()
    {
        var sendDate = DateTime.Now;
        // 用Interlocked保证计数操作的原子性
        Interlocked.Increment(ref _queuePosition);
        await _semaphore.WaitAsync();
        try
        {
            var startProcess = DateTime.Now;
            // 用Task.Delay替代Thread.Sleep,避免阻塞线程池线程
            await Task.Delay(10000);
            Email.SendMail($"Queue entry date: {sendDate} | Process Start Date: {startProcess} | Submission date: {DateTime.Now} | Queued requests: {_queuePosition}");
        }
        finally
        {
            _semaphore.Release();
            // 原子操作减少队列位置
            Interlocked.Decrement(ref _queuePosition);
        }
    }
}

内容的提问来源于stack exchange,提问作者Gabriel de Almeida Alves Pinto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 10:40:49