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

为何锁定的Parallel.Foreach不会立即死锁?及并行循环死锁疑问

为什么Parallel.Foreach会引发死锁,且执行次数超预期?

首先我得补全你大概率用到的测试代码(因为你贴的Test类没写完),假设你的代码框架是这样的:

class Test
{
    private readonly object _lockObj = new object();
    private int _internalCounter = 0;

    // 带锁的Getter
    public int Counter
    {
        get
        {
            lock (_lockObj)
            {
                // 模拟锁占用时间较长的操作
                Thread.Sleep(50);
                return _internalCounter++;
            }
        }
    }

    public void RunTest()
    {
        // 传入包含2个元素的集合
        var testItems = new List<int> { 1, 2 };
        
        Parallel.ForEach(testItems, item =>
        {
            Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 获取Counter值:{Counter}");
            // 若此处还有其他锁操作,死锁概率会大幅提升
        });
    }
}

接下来分两个核心问题拆解:

一、为什么Parallel.Foreach会死锁,普通for循环却不会?

你提到「嵌套锁不会引发问题」这个结论是对的——CLR的lock是可重入锁,同一个线程可以多次获取同一个锁,不会出现自己卡自己的情况。问题的核心在于Parallel.Foreach的多线程特性:

  • 普通for循环是单线程串行执行:所有对Counter的调用都来自同一个线程,哪怕getter里嵌套了多层锁,也不会有跨线程的锁竞争,自然不会死锁。
  • Parallel.Foreach是多线程并行执行:它会从线程池调度多个线程处理集合元素。如果你的Counter getter锁持有时间过长(比如示例里的Thread.Sleep(50)),再加上代码中存在交叉锁场景(比如线程A先锁了对象A再拿_lockObj,线程B先锁了_lockObj再拿对象A),就会形成经典的交叉锁死锁——两个线程各自持有对方需要的锁,互相等待,永远无法推进。

哪怕只有这一个锁,也可能出现「类死锁阻塞」:如果线程池线程被耗尽,持有锁的线程被挂起,其他等待锁的线程也无法继续,看起来和死锁表现一致。

二、为什么执行次数会超过两次?

集合明明只有2个元素,却出现超次数执行,大概率是这几个原因:

  • 委托内多次调用了Counter:如果Parallel.Foreach的委托逻辑里不止一次调用Counter(比如写了两次var val = Counter;),自然会触发更多次的getter执行。
  • 锁对象错误导致的线程安全问题:如果你的锁对象是值类型(比如int),或者是共享的静态对象,会导致多个独立的锁实例,多个线程能同时进入getter,_internalCounter被多次递增,看起来执行次数变多。
  • Parallel分区器的特殊调度:对于极小集合(比如2个元素),默认分区器可能会创建多个分区,极端情况下会出现重复调度迭代任务(这种情况非常少见)。

修复建议

  • 避免在Parallel.Foreach的并行委托里调用带锁方法,对于计数器这种场景,用更轻量的线程安全方案替代lock:
    private int _internalCounter = 0;
    public int Counter => Interlocked.Increment(ref _internalCounter);
    
  • 如果必须用锁,严格保证所有线程获取锁的顺序一致,杜绝交叉锁场景。
  • 把锁的范围缩到最小,只在必要的代码块上加锁,减少锁持有时间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:00:35