SemaphoreSlim 使用简单说明
SemaphoreSlim 是 .NET 中用于控制对共享资源访问的轻量级同步原语。它特别适用于限制并发访问某个资源或操作的数量。
基本概念
SemaphoreSlim 维护一个计数器,表示可以同时访问资源的线程数量。当一个线程获取信号量时,计数器递减;当线程释放信号量时,计数器递增。
构造函数
// 创建具有指定初始计数和最大计数的 SemaphoreSlim 实例
public SemaphoreSlim(int initialCount, int maxCount)
// 创建具有指定初始计数的 SemaphoreSlim 实例(最大计数等于初始计数)
public SemaphoreSlim(int initialCount)
主要方法
WaitAsync() - 异步等待
await semaphore.WaitAsync();
// 或带超时
await semaphore.WaitAsync(TimeSpan.FromSeconds(5));
Release() - 释放信号量
semaphore.Release(); // 释放一个许可证
semaphore.Release(2); // 释放多个许可证
Wait() - 同步等待(不推荐在异步代码中使用)
semaphore.Wait(); // 阻塞直到获得信号量
使用示例
限制并发任务数量
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
// 限制最多同时执行 3 个任务
private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(3, 3);
static async Task Main(string[] args)
{
var tasks = new Task[10];
for (int i = 0; i < 10; i++)
{
int taskId = i;
tasks[i] = ExecuteTask(taskId);
}
await Task.WhenAll(tasks);
}
static async Task ExecuteTask(int taskId)
{
// 等待获取信号量(最多允许 3 个并发)
await semaphore.WaitAsync();
try
{
Console.WriteLine($"Task {taskId} 开始执行");
// 模拟工作
await Task.Delay(1000);
Console.WriteLine($"Task {taskId} 执行完成");
}
finally
{
// 释放信号量,允许其他任务继续
semaphore.Release();
}
}
}
在 using 语句中安全使用
static async Task ExecuteTaskSafely(int taskId)
{
using (await SemaphoreDisposable.AcquireAsync(semaphore))
{
Console.WriteLine($"Task {taskId} 开始执行");
await Task.Delay(1000);
Console.WriteLine($"Task {taskId} 执行完成");
} // 自动释放信号量
}
// 辅助类确保总是释放信号量
public class SemaphoreDisposable : IDisposable
{
private readonly SemaphoreSlim _semaphore;
private SemaphoreDisposable(SemaphoreSlim semaphore)
{
_semaphore = semaphore;
}
public void Dispose()
{
_semaphore.Release();
}
public static async Task<SemaphoreDisposable> AcquireAsync(SemaphoreSlim semaphore)
{
await semaphore.WaitAsync();
return new SemaphoreDisposable(semaphore);
}
}
最佳实践
- 优先使用异步方法: 在异步代码中使用
WaitAsync()而不是Wait() - 确保释放: 使用
try-finally或using语句确保信号量被正确释放 - 合理设置容量: 根据系统资源和性能要求设置合适的信号量容量
- 避免长时间持有: 尽快释放信号量,减少其他线程的等待时间
注意事项
SemaphoreSlim是线程安全的- 适合短时间持有的场景
- 不支持命名实例(如果需要跨进程同步,请使用
Semaphore类) - 记得在不再需要时调用
Dispose()方法释放资源