You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Monitor.Wait与Monitor.Pulse实现自定义Semaphore信号量

基于Monitor实现自定义Semaphore方案

核心实现逻辑

  • 维护线程安全的可用资源计数、最大资源上限
  • 使用Monitor.Wait()实现无可用资源时的线程阻塞
  • 使用Monitor.Pulse()实现资源释放后唤醒等待线程

自定义Semaphore实现代码

public class CustomSemaphore
{
    private readonly object _lockObj = new object();
    private int _currentCount;
    private readonly int _maxCount;

    public CustomSemaphore(int initialCount, int maxCount)
    {
        if (initialCount < 0 || initialCount > maxCount)
            throw new ArgumentOutOfRangeException(nameof(initialCount), "初始计数不能小于0,也不能大于最大计数");
        if (maxCount < 1)
            throw new ArgumentOutOfRangeException(nameof(maxCount), "最大计数不能小于1");
        _currentCount = initialCount;
        _maxCount = maxCount;
    }

    /// <summary>
    /// 阻塞等待直到有资源被释放
    /// </summary>
    public void Request()
    {
        lock (_lockObj)
        {
            // 循环判断防止虚假唤醒
            while (_currentCount == 0)
            {
                Monitor.Wait(_lockObj);
            }
            _currentCount--;
        }
    }

    /// <summary>
    /// 释放占用的资源
    /// </summary>
    public void Release()
    {
        lock (_lockObj)
        {
            if (_currentCount >= _maxCount)
                throw new SemaphoreFullException("释放资源后超过信号量最大容量");
            _currentCount++;
            // 唤醒一个等待的线程
            Monitor.Pulse(_lockObj);
        }
    }

    /// <summary>
    /// 返回当前可用资源的数量
    /// </summary>
    public int NumberAvailable()
    {
        lock (_lockObj)
        {
            return _currentCount;
        }
    }
}

调整后的业务代码

替换原有SemaphoreSlim为自定义CustomSemaphore即可,改动量极小:

class Program
{
    private const int InitialCount = 2;
    private const int MaximumCount = 8;

    static void Main()
    {
        var semaphoreObject = new CustomSemaphore(InitialCount, MaximumCount);
        var printerObject = new Printer();

        for (int i = 0; i < 20; ++i)
        {
            int j = i;
            Task.Factory.StartNew(() =>
            {
                Request(semaphoreObject);
                printerObject.Print(j);
                Release(semaphoreObject);
                NumberAvailable(semaphoreObject);
            });
        }
        Console.ReadLine();
    }

    private static void Request(CustomSemaphore s)
    {
        s.Request();
    }

    private static void Release(CustomSemaphore s)
    {
        s.Release();
    }

    private static void NumberAvailable(CustomSemaphore s)
    {
        Console.WriteLine($"Available resource: {s.NumberAvailable()}");
    }
}

class Printer
{
    public void Print(int documentToPrint)
    {
        Console.WriteLine("Printing document: " + documentToPrint);
        Thread.Sleep(TimeSpan.FromSeconds(5));
    }
}

注意事项

  • Request方法中使用while循环判断可用资源数,是为了避免线程的虚假唤醒问题
  • 所有涉及计数读写的操作都加了锁,保证多线程场景下计数的准确性
  • 增加了参数合法性校验,避免非法输入导致的逻辑错误

内容的提问来源于stack exchange,提问作者user122222

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 11:45:03