死锁,Parallel.ForEach周围和内部都有锁

本文关键字:内部 周围 Parallel ForEach 死锁 | 更新日期: 2023-09-27 18:21:50

你能解释一下为什么这段代码死锁吗?

int[] testlist = new int[ ] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
lock ( testlock ) {
    Parallel.ForEach( testlist, new ParallelOptions( ) { MaxDegreeOfParallelism = 90 }, ( int i ) => {
        Console.WriteLine( "hi there before " + i + " " + Monitor.IsEntered( testlock ) );
        lock ( testlock ) {
            Console.WriteLine( "hi there inner " + i + " " + Monitor.IsEntered( testlock ) );
        }
        Console.WriteLine( "hi there after " + i + " " + Monitor.IsEntered( testlock ) );
    } );
}

当然,如果没有周围的锁,这段代码就不会死锁。

编辑:

感谢您的解释。典型输出为:

hi there before 3 True
hi there inner 3 True
hi there after 3 True
hi there before 4 True
hi there inner 4 True
hi there after 4 True
hi there before 5 True
hi there inner 5 True
hi there after 5 True
hi there before 6 True
hi there inner 6 True
hi there after 6 True
hi there before 7 True
hi there inner 7 True
hi there after 7 True
hi there before 8 True
hi there inner 8 True
hi there after 8 True
hi there before 9 True
hi there inner 9 True
hi there after 9 True
hi there before 10 True
hi there inner 10 True
hi there after 10 True

事实上,典型的执行涉及我的机器上的两个线程:一个被阻塞等待锁定("1"(,另一个正在运行其他迭代(从 3 到 10,注意输出的线性(。"1"线程永远等待。现在很清楚了,谢谢!

死锁,Parallel.ForEach周围和内部都有锁

这是行不通的,因为您肯定在这里造成了死锁:

外部lock(testlock)锁定testlock,然后Parallel.ForEach开始在多个线程中处理testlist。现在这些线程到达内部lock(testlock),但由于它仍然被线程中的外部线程锁定,因此无法再次锁定它=>外部lock在处理完成之前不会释放,但是处理无法完成,直到外部lock释放->死锁...

您可以使用MaxDegreeOfParallelism = 1轻松验证这一点: 然后一切都在主线程中运行,级联锁不会死锁,因为它可以再次锁定,因为它是同一个线程。

你想做什么 - 如果你解释一下,我们可以提供帮助吗?

注意:
如果您的系统没有 90!CPU 对使用MaxDegreeOfParallelism = 90的性能没有帮助 - 建议将该数字保持较小或等于您的 CPU/内核计数(您可以通过 Environment.ProcessorCount 获得(。

您在开始Parallel.ForEach之前已经锁定了testLock(第一个锁定(。在 Parallel.ForEach 中,您可以锁定同一对象。ForEach 在某个时间点使用不同的线程,而不是采用第一个锁的线程。该线程将等到锁可用,但它将永远不可用,因为在 ForEach 完成之前不会释放第一个锁。

由于ForEach在所有线程完成之前不会退出,因此最终会陷入死锁。