I came across this situation where the following plinq statement inside static constructor gets deadlocked:
static void Main(string[] args)
{
new Blah();
}
class Blah
{
static Blah()
{
Enumerable.Range(1, 10000)
.AsParallel()
.Select(n => n * 3)
.ToList();
}
}
It happens only when a constructor is static. Can someone explain this to me please.
Is it TPL bug? Compiler? Me?
While the reason has already been explained as to why you wouldn't want to do threaded work inside a static constructor, I thought I'd add that the "right" way to do this instead would be with a static
Lazy<T>
. This is also more efficient as the work to generate those resources will be defferred until those resources are actually needed.For what its worth, the issue does not arise on Mono:
Do you have a windows compiled binary so I can compare the generated MSIL? I'm not convinced this is a library-only issue, and I'm curious :)
Comparing the IL was a bit messy, so I decided to just try both binaries on both platforms. Hehe I revived my old Windows virtual machine just to test this :)
Running the VS compiled binaries on Mono is no problem. You could try it on windows using 2.10.1 (http://www.go-mono.com/mono-downloads/download.html), only 77.4Mb :)
(I used a custom built mono 2.11 on linux so it could be that the feature support is not complete yet)
I also noticed that when running on windows, a CTRL-C is able to break out of the lock. Will post if I find some more to this.
Update 2
Well, installing Mono runs circles around installing installing VSExpress even on windows. Installing mono has finished in 4 minutes, and resulted in:
No deadlock :) Now all that remains is waiting for VSExpress to be installed (forever) and istall debugging tools (unknown) and than have a crack at it (probably till late night). CU later
It is generally dangerous to call threading code from a static constructor. In order to ensure that the static constructor executes only once, the CLR executes the static constructor under a lock. If the thread running the static constructor waits on a helper thread, there is a risk that the helper thread is going to need the CLR-internal lock for some reason too, and the program will deadlock.
Here is a simpler code sample that demonstrates the problem:
Section 10.5.3.3 "Races and deadlocks" of the ECMA CLI spec guarantees the following:
So, a type initializer (i.e., a static constructor) will not deadlock, provided that no operation in the static constructor blocks the thread. If the static constructor does block, it risks a deadlock.