吴晓阳
发布于 2026-03-20 / 2 阅读
0
0

轻量级信号量SemaphoreSlim 使用简单说明

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

最佳实践

  1. 优先使用异步方法: 在异步代码中使用 WaitAsync() 而不是 Wait()
  2. 确保释放: 使用 try-finallyusing 语句确保信号量被正确释放
  3. 合理设置容量: 根据系统资源和性能要求设置合适的信号量容量
  4. 避免长时间持有: 尽快释放信号量,减少其他线程的等待时间

注意事项

  • SemaphoreSlim 是线程安全的
  • 适合短时间持有的场景
  • 不支持命名实例(如果需要跨进程同步,请使用 Semaphore 类)
  • 记得在不再需要时调用 Dispose() 方法释放资源

评论