跳转到主内容
极星编程网:以代码为星,赴技术山海!

c# AsyncSemaphore 和 SemaphoreSlim 的区别

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);

await semaphore.WaitAsync(cancellationToken); try { // 执行受保护的异步操作 await SomeIoOperationAsync(); } finally { semaphore.Release(); }

注意要点:
WaitAsync()
支持
CancellationToken
,能响应取消请求(
Semaphore
不支持) 必须配对
Release()
,否则计数泄漏 → 后续所有
WaitAsync()
都会永久挂起 不要在
catch
块外释放 ——
try/finally
是底线,
using
不适用(它没实现
IAsyncDisposable
) 什么时候才该考虑封装一个“AsyncSemaphore”? 仅当你的项目反复出现以下模式,且团队多人容易写错时: C知道 CSDN推出的一款AI技术问答工具 下载 每次都要手写
try/finally
+
Release()
经常忘记传
cancellationToken
,导致无法取消等待 需要统一记录等待超时、统计并发峰值等可观测性逻辑 此时可自己写一个极简封装(20 行内),例如:
public sealed class AsyncSemaphore : IAsyncDisposable { private readonly SemaphoreSlim _semaphore;
public AsyncSemaphore(int initialCount) => _semaphore = new SemaphoreSlim(initialCount);

public async ValueTask 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 住。

相关文章