Plinq语句在静态构造函数中陷入僵局

我遇到了这种情况,其中静态构造函数中的以下plinq语句变得死锁:

static void Main(string[] args) { new Blah(); } class Blah { static Blah() { Enumerable.Range(1, 10000) .AsParallel() .Select(n => n * 3) .ToList(); } } 

它只在构造函数是静态时才会发生。 请有人向我解释一下。

是TPL错误吗? 编译器? 我?

从静态构造函数调用线程代码通常很危险。 为了确保静态构造函数只执行一次,CLR在锁定下执行静态构造函数。 如果运行静态构造函数的线程在辅助线程上等待,那么由于某种原因,辅助线程也可能需要CLR内部锁,并且程序将死锁。

这是一个更简单的代码示例,演示了这个问题:

 using System.Threading; class Blah { static void Main() { /* Won't run because the static constructor deadlocks. */ } static Blah() { Thread thread = new Thread(ThreadBody); thread.Start(); thread.Join(); } static void ThreadBody() { } } 

第10.5.3.3节ECMA CLI规范的 “种族和死锁”保证以下内容:

除非从类型初始化程序(直接或间接)调用的某些代码明确调用阻塞操作,否则单独的类型初始化不应创建死锁。

因此,如果静态构造函数中的操作没有阻塞线程,则类型初始值设定项(即静态构造函数)将不会死锁。 如果静态构造函数阻塞,则存在死锁的风险。

虽然已经解释了为什么你不想在静态构造函数中进行线程工作的原因,但我想我会补充说“正确”的方法是使用静态Lazy 。 这也更有效,因为生成这些资源的工作将被确定,直到实际需要这些资源。

 class Blah { // Supply factory method to generate the numbers, but actual generation will be deferred private static Lazy> MyMagicNumbers = new Lazy>(Blah.GenerateMagicNumbers); public void DoSomethingWithMagicNumbers() { // Call to Lazy.Value will synchronize any calling threads until value is initially generated from the factory List magicNumbers = Blah.MyMagicNumbers.Value; // ... do something here ... } private List GenerateMagicNumbers() { return Enumerable.Range(1, 10000) .AsParallel() .Select(n => n * 3) .ToList(); } } 

对于它的价值,Mono上不会出现这个问题:

 [mono] /tmp @ dmcs par.cs [mono] /tmp @ mono ./par.exe 

你有一个Windows编译二进制文件,所以我可以比较生成的MSIL吗? 我不相信这是一个仅限图书馆的问题,我很好奇:)


比较IL有点乱,所以我决定在两个平台上尝试两个二进制文件。 呵呵我复活了我的旧Windows虚拟机只是为了测试这个:)

在Mono上运行VS编译的二进制文件没问题。 您可以使用2.10.1(http://www.go-mono.com/mono-downloads/download.html)在Windows上试用它,只有77.4Mb 🙂

我在linux上使用了自定义的单声道2.11,因此function支持尚未完成

  \ run on platform: MS.Net 4.0 Mono 2.1x built on: -------------+---------------------------------------- Visual Studio | deadlock no deadlock | MonoDevelop | deadlock no deadlock 

我还注意到,当在Windows上运行时,CTRL-C能够突破锁定。 如果我发现更多内容,请发布。


更新2

好吧,安装Mono即使在Windows上安装VSExpress也会运行。 安装mono已在4分钟内完成,并导致:

 C:\Users\Seth>"c:\Program Files (x86)\Mono-2.10.1\bin\mono.exe" ConsoleApplication2.exe C:\Users\Seth> 

没有死锁:)现在剩下的就是等待VSExpress安装(永远)和istall调试工具(未知),而不是有一个裂缝(可能直到深夜)。 再见