SemaphoreSlim 是 .NET 专为异步并发控制设计的轻量信号量,支持 await、不阻塞线程,需单例注册、配超时、在 try/finally 或 using 中确保 Release,避免许可证泄漏。

用 SemaphoreSlim 控制并发请求数最直接
不是靠加锁或队列模拟,SemaphoreSlim 是 .NET 专为异步并发控制设计的轻量信号量。它支持 await,不会阻塞线程,适合 Web API 场景。
常见错误是误用 lock 或 Monitor —— 它们同步阻塞,一卡全卡,高并发下线程池迅速耗尽,反而拖垮服务。
- 初始化时传入最大并发数,比如
new SemaphoreSlim(5)表示最多 5 个请求同时执行 - 必须在
try/finally或using中释放,否则许可证永久泄漏,后续所有请求都会卡死在WaitAsync() - 设置超时(如
await sem.WaitAsync(TimeSpan.FromSeconds(3))),避免某个慢请求长期占着许可证
ASP.NET Core 中全局限制接口并发的写法
别在每个 Controller 方法里手写 WaitAsync —— 容易漏、难维护、无法统一降级策略。推荐用中间件或 ActionFilter。
使用场景:突发流量打爆下游依赖(比如数据库连接池、第三方 HTTP 接口),需要快速限流而非等超时。
- 在
Program.cs注册单例SemaphoreSlim:services.AddSingleton(new SemaphoreSlim(10)) - 写一个
ActionFilter,在OnActionExecutionAsync里await _sem.WaitAsync(),执行完_sem.Release() - 注意不要对健康检查、静态资源等路径做限制,否则
/health也卡住,运维排查更困难
和 Microsoft.Extensions.Concurrency 的区别
这个 NuGet 包里的 TaskLimiter 看起来更“高级”,但它本质是包装了 SemaphoreSlim,额外加了队列和取消逻辑。多数情况下没必要引入。
性能影响很小,但多一层抽象就多一层出问题的可能:比如它默认不限制排队长度,突发流量下任务堆积,内存暴涨;而原生 SemaphoreSlim 配合超时能更快失败、更快反馈。
- 如果你只需要硬性并发数限制(如“绝不允许超过 8 个 DB 查询同时跑”),直接用
SemaphoreSlim - 如果还要按优先级调度、或需要精确统计排队时间,再考虑
TaskLimiter -
TaskLimiter不支持 .NET 6 以下版本,而SemaphoreSlim从 .NET 4.5 就有
别忽略异步上下文和作用域生命周期
在 ASP.NET Core 里,SemaphoreSlim 实例必须是单例(Singleton)。如果注册成 Scoped,每个请求都拿到新实例,限流完全失效。
另一个坑是:在 async void 方法或后台任务里用它,没 await 或没处理异常,Release() 就永远不会执行。
- 检查 DI 容器注册方式,确认是
AddSingleton(...) - 所有
WaitAsync调用点,确保对应位置有Release(),且不在catch块外提前 return - 日志里加一句 “acquired / released” 打点,上线后看比例是否接近 1:1,能快速发现泄漏
真正难的不是写那几行 WaitAsync,而是保证它在所有路径下都被正确获取和释放——尤其跨多个 try/catch、有重试逻辑、或调用了其他封装层的时候。











