SemaphoreSlim 是 .NET 中一个轻量级的同步原语,用于控制对资源池或代码块的并发访问。相比传统的 Semaphore 类,它更适用于进程内同步且支持异步操作,是处理异步/等待模式的理想选择。

核心特性:

  • 轻量级(内存占用约 1/4 的 Semaphore)
  • 支持同步和异步等待(WaitAsync)
  • 无需命名,仅用于进程内同步
  • 性能优化:在 .NET Core+ 中有显著提升
  • 可设置初始许可数和最大许可数

典型应用场景:

  1. 限制资源池并发访问(如数据库连接)
  2. API 请求速率控制
  3. 批量任务并行度控制
  4. 异步流水线中的流量控制

基础使用示例:

// 同步示例:限制最多 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);

高级技巧:

  1. 动态调整许可数:
// 从初始 2 许可扩展到最大 5 许可
var dynamicSemaphore = new SemaphoreSlim(2, 5);

// 需要更多许可时:
if (dynamicSemaphore.CurrentCount < 5)
    dynamicSemaphore.Release(3); // 谨慎操作!
  1. 带超时的异步等待:
if (await asyncSemaphore.WaitAsync(TimeSpan.FromSeconds(5)))
{
    // 获取许可成功
}
else
{
    // 超时处理
}
  1. 组合使用 CancellationToken:
var cts = new CancellationTokenSource(3000); // 3秒超时
try
{
    await asyncSemaphore.WaitAsync(cts.Token);
}
catch (OperationCanceledException)
{
    // 取消处理
}

最佳实践:

  1. 始终使用

    try-finally

    确保 Release() 被调用

  2. 异步场景优先使用

    WaitAsync

    而非同步 Wait

  3. 许可数设置需结合实际资源容量(如数据库连接池大小)
  4. 避免在持有信号量时执行长时间阻塞操作
  5. 监控 CurrentCount 进行健康检查

性能对比:
在 .NET 6 中,SemaphoreSlim 的同步操作比 Monitor 快约 2x,异步操作比 Semaphore 快约 5x。但在跨进程场景仍需使用传统的 Semaphore 类。

最后修改:2025 年 02 月 14 日
如果觉得我的文章对你有用,请随意赞赏