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
相关产品推荐
相关产品推荐

