静态构造函数中的Task.Run

13

考虑下面的代码。

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会导致程序永久冻结。为什么?

2个回答

14

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

基本上,在类初始化完成之前,类X中的任何内容都不能使用。但是你的匿名方法() => 0被编译为该类的成员。类的初始化不会完成,直到Task完成,但是Task无法完成,因为它依赖于一个在初始化类完成之前不允许运行的方法。

死锁。

您的示例显然是人为的,因此无法提供如何修复您的实际问题的建议。在这个特定的示例中,可以将初始化替换为Task.FromResult(0).Result;,但当然更加人为;如果那真的可用,你只需将0分配给字段。

但是,无论您的实际场景如何,修复它的方式是不要创建一种情况,其中类的初始化取决于某些外部组件需要该类才能完成。例如,您可以考虑使用Lazy<T>来初始化值,或者直接调用方法(这是允许的)。

无论一个示例是否人为,都没有必要启动一个Task,然后立即阻塞当前线程,直到它完成。因此,如果您有任何代码,虽然不是字面上的例子,但实际上做的事情类似,显而易见的解决方法是将其更改为以串行单线程方式执行。


这是我不熟悉的静态类初始化和类初始化锁的内部运作方式。我遇到的实际情况比这个更加复杂,但我能够将其简化为这个小例子。没有问题找出更好的解决方案或变通方法。感谢解释! - Timothy Shields

0

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

基本上,在类X初始化之前,不能使用该类中的任何内容。


但是任务无法完成,因为它依赖于一个在类初始化完成之前不允许运行的方法。
如果是这样,在这种情况下,您应该收到编译器错误而不是运行时死锁。
无论如何,这段代码是合法的。
static class X
{
    public static int Value = Method();
    private static int Method()
    {
        return 0;
    }
}

这里是问题的解释。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接