asyncsemaphore 不是 .net 原生类型,而是社区基于 semaphoreslim 封装的语法糖;semaphoreslim 本身已原生支持异步等待(waitasync)和取消令牌,直接使用更简可控。

没有 AsyncSemaphore 这个类型 —— 它是社区封装的“语法糖”,不是 .NET 原生类;而 SemaphoreSlim 是官方提供的、支持异步等待的轻量信号量。
为什么搜不到 AsyncSemaphore 的官方文档?
因为 AsyncSemaphore 并不存在于 System.Threading 或任何 .NET 标准库中。它是开发者(比如在 NuGet 上的 microsoft.visualstudio.Threading 或某些开源项目)基于 SemaphoreSlim 封装的一层薄包装,目的通常是:
- 把
WaitAsync()+using模式固化成await using友好结构 - 自动处理
Release()(比如用IDisposable或IAsyncDisposable) - 隐藏
CancellationToken传递细节
它不解决新问题,只是让常见用法更安全、不易漏 Release。
SemaphoreSlim 本身已原生支持异步:别绕路封装
.NET Framework 4.5+ 和所有 .NET Core / .NET 5+ 都自带 SemaphoreSlim.WaitAsync(),无需额外引入“AsyncSemaphore”类。直接用它就是最简、最可控的方式:
var semaphore = new SemaphoreSlim(initialCount: 3, maxCount: 3); <p>await semaphore.WaitAsync(cancellationToken); try { // 执行受保护的异步操作 await SomeIoOperationAsync(); } finally { semaphore.Release(); }
注意要点:
-
WaitAsync()支持CancellationToken,能响应取消请求(Semaphore不支持) - 必须配对
Release(),否则计数泄漏 → 后续所有WaitAsync()都会永久挂起 - 不要在
catch块外释放 ——try/finally是底线,using不适用(它没实现IAsyncDisposable)
什么时候才该考虑封装一个“AsyncSemaphore”?
仅当你的项目反复出现以下模式,且团队多人容易写错时:
- 每次都要手写
try/finally+Release() - 经常忘记传
cancellationToken,导致无法取消等待 - 需要统一记录等待超时、统计并发峰值等可观测性逻辑
此时可自己写一个极简封装(20 行内),例如:
public sealed class AsyncSemaphore : IAsyncDisposable { private readonly SemaphoreSlim _semaphore; <pre class='brush:php;toolbar:false;'>public AsyncSemaphore(int initialCount) => _semaphore = new SemaphoreSlim(initialCount); public async ValueTask<IDisposable> EnterAsync(CancellationToken ct = default) { await _semaphore.WaitAsync(ct); return new Releaser(_semaphore); } private struct Releaser : IDisposable { private readonly SemaphoreSlim _s; public Releaser(SemaphoreSlim s) => _s = s; public void Dispose() => _s.Release(); } public async ValueTask DisposeAsync() => await Task.CompletedTask;
}
但请记住:这仍是建立在 SemaphoreSlim 之上的便利层,不是替代品。
真正容易被忽略的是:无论用原生 SemaphoreSlim 还是自封的 AsyncSemaphore,都必须确保 Release() 在 *每一次* WaitAsync() 成功后被执行 —— 缺一次,整个信号量就卡死。这不是异常,也不会报错,只会让后续所有线程/任务无声地 hang 住。