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 类。