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

Task.WaitAll未按预期抛出异常,任务全部挂起问题

SemaphoreSlim超量Release后Task.WaitAll未抛异常反而任务全部挂起的问题

我原本预期当SemaphoreSlim达到maxCount时,Task.WaitAll会抛出异常,但实际结果是所有任务都挂起了。

代码如下:

SemaphoreSlim semaphore = new(initialCount: 3, maxCount: 10);

var tasks = Enumerable.Range(1, 20).Select(_ =>
{
    return Task.Run(() =>
    {
        Console.WriteLine($"getting started for task {Task.CurrentId}");
        semaphore.Wait();
        int prevCount = semaphore.Release(10000000000000000); // should throw here
        Console.WriteLine($"prev: {prevCount}\ncurrent: {semaphore.CurrentCount}");
    });
});

Task.WaitAll(tasks); // shouldn't it throw here?

编辑补充:我猜测这是因为Task.WaitAll会等待所有任务完成,即便有任务已抛出异常。那么在异常发生后,其他线程仍在等待信号量,但此时信号量已不可用?


问题原因拆解

  • 任务内异常不会中断WaitAll
    Release(10000000000000000)确实会触发SemaphoreFullException,但这个异常只属于抛出它的单个任务,不会直接终止Task.WaitAll的等待逻辑。Task.WaitAll必须等到**所有任务都完成(正常或异常)**才会结束,而这里大部分任务根本没机会完成。

  • 未归还的许可导致死锁
    每个任务执行semaphore.Wait()时会消耗一个许可,初始的3个许可会被前3个任务抢占。当这3个任务执行到Release时,都会因参数过大抛出异常,但异常发生前消耗的许可没有被归还——因为Release操作还没成功执行就抛出了异常,相当于这3个许可被永久占用。此时信号量可用许可为0,剩下的17个任务会一直卡在semaphore.Wait()上,永远无法获取许可完成任务,最终导致Task.WaitAll无限等待,程序挂起。

  • WaitAll不会提前抛异常
    只有当所有任务都进入完成状态(RanToCompletion/Faulted/Canceled),Task.WaitAll才会抛出包含所有任务异常的AggregateException。但这里大量任务处于Running状态(卡在Wait),所以WaitAll会一直等下去,不会抛出异常。

验证与修复示例

如果给任务加上try-finally确保许可被归还,就能看到Task.WaitAll最终抛出异常:

SemaphoreSlim semaphore = new(initialCount: 3, maxCount: 10);

var tasks = Enumerable.Range(1, 20).Select(_ =>
{
    return Task.Run(() =>
    {
        Console.WriteLine($"getting started for task {Task.CurrentId}");
        semaphore.Wait();
        try
        {
            int prevCount = semaphore.Release(10000000000000000);
            Console.WriteLine($"prev: {prevCount}\ncurrent: {semaphore.CurrentCount}");
        }
        finally
        {
            // 无论是否异常,归还获取的许可
            semaphore.Release();
        }
    });
});

Task.WaitAll(tasks); // 此时所有任务都会Faulted,WaitAll会抛出AggregateException

要彻底解决问题,需避免调用Release时传入会让CurrentCount超过maxCount的数值,或者在调用前检查当前可用许可数。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:53:12