2014-12-23 155 views
13

请考虑以下代码。静态初始化程序中的Task.Run

static class X 
{ 
    public static int Value = Task.Run(() => 0).Result; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var value = X.Value; 
    } 
} 

调用Task.Run,然后在静态初始化Result导致程序冻结永久。为什么?

回答

14

您正在看到CLR的类初始化锁死锁。

基本上,在类初始化之前,类X中的任何内容都可以使用。但是您的匿名方法() => 0已编译为类的成员。直到Task可以完成,类初始化才会完成,但Task无法完成,因为它取决于在类的初始化完成之前不允许运行的方法。

死锁。

你的例子很明显,所以不可能就如何解决你的现实问题提供建议。在这个特定的例子中,你可以用Task.FromResult(0).Result;代替初始化,但当然这更加人为化;如果这实际上可用,则只需将0分配给该字段。

但是无论你的现实世界的场景是什么,解决这个问题的方法是不要创建一个类的初始化依赖于一些需要该类来完成的外部组件。例如,您可以考虑使用Lazy<T>来初始化该值,或者直接调用该方法(这将被允许)。

无论是否设计了一个示例,启动Task只会立即阻止当前线程,直到完成为止。所以,如果你有任何代码虽然不像这个例子那样完全像这样,但仍然有效地执行相同的事情,但明显的解决方法是将它改为以串行,单线程方式执行。

+0

这是静态类初始化和我不熟悉的类初始化锁的内部工作。我遇到的实际情况是比这更复杂的代码,但我能够将它缩小到这个小例子。没有麻烦提出更好的解决方案或解决方法。感谢您的解释! –

0

我认为这个问题的解释是不正确的。

基本上,类X中的任何内容都可以在类初始化之前使用。


但任务不能完成,因为它依赖于未 允许运行,直到类的初始化完成的方法。

如果是这样,在这种情况下,您应该得到一个编译器错误,但在运行时不会死锁。

反正这个代码是合法的

static class X 
{ 
    public static int Value = Method(); 
    private static int Method() 
    { 
     return 0; 
    } 
} 

Here是问题的解释。