SemaphoreSlim 是 .NET 中一个轻量级的同步原语,用于控制对资源池或代码块的并发访问。相比传统的 Semaphore 类,它更适用于进程内同步且支持异步操作,是处理异步/等待模式的理想选择。
核心特性:
- 轻量级(内存占用约 1/4 的 Semaphore)
- 支持同步和异步等待(WaitAsync)
- 无需命名,仅用于进程内同步
- 性能优化:在 .NET Core+ 中有显著提升
- 可设置初始许可数和最大许可数
典型应用场景:
- 限制资源池并发访问(如数据库连接)
- API 请求速率控制
- 批量任务并行度控制
- 异步流水线中的流量控制
基础使用示例:
// 同步示例:限制最多 3 个并发访问
var semaphore = new SemaphoreSlim(3, 3); 
void AccessResource(int id)
{
    semaphore.Wait();
    try
    {
        Console.WriteLine($"{id} 进入临界区 [{DateTime.Now:HH:mm:ss}]");
        Thread.Sleep(2000); // 模拟工作
    }
    finally
    {
        semaphore.Release();
        Console.WriteLine($"{id} 离开临界区 [{DateTime.Now:HH:mm:ss}]");
    }
}
// 并行测试(5个线程竞争3个许可)
Parallel.For(0, 5, i => AccessResource(i));异步使用示例(推荐 ASP.NET Core 场景):
// 异步示例:限制最多 2 个并发 API 请求
var asyncSemaphore = new SemaphoreSlim(2, 2);
async Task CallApiAsync(int requestId)
{
    await asyncSemaphore.WaitAsync();
    try
    {
        Console.WriteLine($"请求 {requestId} 开始处理 [{DateTime.Now:HH:mm:ss}]");
        await Task.Delay(1000); // 模拟 API 调用
        Console.WriteLine($"请求 {requestId} 完成 [{DateTime.Now:HH:mm:ss}]");
    }
    finally
    {
        asyncSemaphore.Release();
    }
}
// 模拟 5 个并发请求
var tasks = Enumerable.Range(1, 5)
                     .Select(i => CallApiAsync(i));
await Task.WhenAll(tasks);高级技巧:
- 动态调整许可数:
// 从初始 2 许可扩展到最大 5 许可
var dynamicSemaphore = new SemaphoreSlim(2, 5);
// 需要更多许可时:
if (dynamicSemaphore.CurrentCount < 5)
    dynamicSemaphore.Release(3); // 谨慎操作!- 带超时的异步等待:
if (await asyncSemaphore.WaitAsync(TimeSpan.FromSeconds(5)))
{
    // 获取许可成功
}
else
{
    // 超时处理
}- 组合使用 CancellationToken:
var cts = new CancellationTokenSource(3000); // 3秒超时
try
{
    await asyncSemaphore.WaitAsync(cts.Token);
}
catch (OperationCanceledException)
{
    // 取消处理
}最佳实践:
- 始终使用 - try-finally- 确保 Release() 被调用 
- 异步场景优先使用 - WaitAsync- 而非同步 Wait 
- 许可数设置需结合实际资源容量(如数据库连接池大小)
- 避免在持有信号量时执行长时间阻塞操作
- 监控 CurrentCount 进行健康检查
性能对比:
在 .NET 6 中,SemaphoreSlim 的同步操作比 Monitor 快约 2x,异步操作比 Semaphore 快约 5x。但在跨进程场景仍需使用传统的 Semaphore 类。