2014-12-23 53 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是問題的解釋。