8

我遇到了这种情况,静态构造函数中的以下 plinq 语句陷入僵局:

static void Main(string[] args)
{
    new Blah();
}

class Blah
{
     static Blah()
     {
         Enumerable.Range(1, 10000)
            .AsParallel()
            .Select(n => n * 3)
            .ToList();
     }
}

仅当构造函数是静态的时才会发生。有人可以向我解释一下吗。

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

4

3 回答 3

9

从静态构造函数调用线程代码通常很危险。为了保证静态构造函数只执行一次,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() { }
}

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

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

因此,类型初始化器(即静态构造器)不会死锁,前提是静态构造器中没有任何操作阻塞线程。如果静态构造函数确实阻塞,它就有死锁的风险。

于 2011-04-25T00:21:21.460 回答
3

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

class Blah
{
    // Supply factory method to generate the numbers, but actual generation will be deferred
    private static Lazy<List<int>> MyMagicNumbers = new Lazy<List<int>>(Blah.GenerateMagicNumbers);

    public void DoSomethingWithMagicNumbers()
    {
        // Call to Lazy<T>.Value will synchronize any calling threads until value is initially generated from the factory
        List<int> magicNumbers = Blah.MyMagicNumbers.Value;

        // ... do something here ...
    }

    private List<int> GenerateMagicNumbers()
    {
        return Enumerable.Range(1, 10000)
                          .AsParallel()
                          .Select(n => n * 3)
                          .ToList();
    }
}
于 2011-04-27T15:39:52.683 回答
2

对于它的价值, 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 上使用了自定义构建的 mono 2.11,因此可能是功能支持尚未完成

     \ 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

好吧,即使在 Windows 上,安装 Mono 也会围绕安装 VSExpress 运行。安装单声道已在 4 分钟内完成,结果如下:

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

没有死锁 :) 现在剩下的就是等待安装 VSExpress(永远)并安装调试工具(未知),然后对其进行破解(可能直到深夜)。再见

于 2011-04-24T12:49:05.083 回答