sgatto
sgatto

Reputation: 119

Deadlock with lock around and inside Parallel.ForEach

Can you please explain why this code deadlocks ?

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 ) );
    } );
}

Of course without the surrounding lock this code do not deadlock.

Edit:

thanks for the explanation. The typical output is:

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

In fact, typical execution involes two threads on my machine: one is blocked waiting for lock (the "1" one) and another is running other iterations (from 3 to 10, note the linearity of output). The "1" thread waits forever. Now it is clear, thanks !

Upvotes: 1

Views: 2124

Answers (2)

vcsjones
vcsjones

Reputation: 141703

You have taken a lock on testLock before starting Parallel.ForEach (1st lock). Inside of your Parallel.ForEach, you take a lock on the same object. The ForEach uses a different thread at some point other than the one that took the 1st lock. That thread is going to wait until the lock is available, however it will never be available because the 1st lock won't get released until the ForEach is complete.

Since ForEach won't exit until all threads are complete, you end up deadlocked.

Upvotes: 3

Christoph Fink
Christoph Fink

Reputation: 23113

This can't work because you are definitely creating a deadlock here:

The outer lock(testlock) locks on testlock and then the Parallel.ForEach starts processing your testlist in multiple threads. Now these threads get to the inner lock(testlock), but as it is still locked by the outer one in its thread it can not lock it again => the outer lock does not release until the processing is done, but the processing can not complete until the outer lock releases -> deadlock...

You can easily verify this by using MaxDegreeOfParallelism = 1: Then everything runs in the main thread and the cascaded lock does not deadlock as it can lock again, because it is the same thread.

What are you trying to do - may we can help if you explain?

NOTE:
If your system does not have 90! CPU's it does NOT help the performance to use MaxDegreeOfParallelism = 90 - it is recommended to keep that number smaller or equal to your CPU/Core count (you can get that via Environment.ProcessorCount).

Upvotes: 5

Related Questions