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

如何实现类似WaitHandle.WaitAny的多SemaphoreSlim异步等待?

异步等待多个信号量的高效实现方案

首先替换原有的Semaphore为SemaphoreSlim(它原生支持异步等待,且完全保留限流能力),再通过Task.WhenAny结合任务包装逻辑,实现类似WaitHandle.WaitAny的异步效果,全程无线程池线程阻塞。

核心实现代码

SemaphoreSlim sem1 = new SemaphoreSlim(1, 1);
SemaphoreSlim sem2 = new SemaphoreSlim(1, 1);

// 给每个信号量的异步等待任务绑定对应索引
var waitTasks = new List<Task<int>>
{
    sem1.WaitAsync().ContinueWith(_ => 0),
    sem2.WaitAsync().ContinueWith(_ => 1)
};

// 等待第一个可用的信号量,获取其索引
int index = await Task.WhenAny(waitTasks).Unwrap();

if (index == 0)
{
    try
    {
        // do some operation
    }
    finally
    {
        sem1.Release(); // 必须释放信号量,避免泄漏
    }
}
else if (index == 1)
{
    try
    {
        // do some other operation
    }
    finally
    {
        sem2.Release();
    }
}

方案细节说明

  • 无阻塞特性:所有等待逻辑均为异步非阻塞,不会占用线程池线程闲置等待信号量,符合异步编程的资源高效利用原则。
  • 限流能力保留:SemaphoreSlim和原Semaphore一样支持设置初始许可数与最大许可数,完全满足限流需求。
  • 索引精准对应:通过ContinueWith为每个WaitAsync任务绑定信号量的索引,确保任务完成后能准确识别是哪个信号量可用。

通用封装(复用场景)

如果需要在多处使用该逻辑,可以封装成通用方法,支持任意数量的SemaphoreSlim:

public static async Task<int> WaitAnyAsync(params SemaphoreSlim[] semaphores)
{
    var waitTasks = semaphores.Select((sem, idx) => 
        sem.WaitAsync().ContinueWith(_ => idx)).ToList();
    
    return await Task.WhenAny(waitTasks).Unwrap();
}

调用示例:

int index = await WaitAnyAsync(sem1, sem2);
// 后续操作逻辑同上

关键注意事项

  • 必须在操作完成后调用Release()释放信号量,建议放在finally块中,防止异常导致信号量泄漏。
  • 如需支持超时,可给WaitAsync传入超时参数(如sem1.WaitAsync(TimeSpan.FromSeconds(3))),此时需扩展包装逻辑,返回包含索引和是否成功获取许可的结果,避免超时后错误执行操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 20:15:43