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

静的コンストラクターからスレッデッドコードを呼び出すことは一般的に危険です。静的コンストラクターが1回だけ実行されるようにするために、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

静的コンストラクター内でスレッド化された作業を行いたくない理由についてはすでに説明しましたが、代わりに静的コンストラクターを使用する「正しい」方法を追加したいと思いました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 

生成された MSIL を比較できるように、Windows でコンパイルされたバイナリはありますか? これがライブラリのみの問題であるとは確信が持てず、興味があります:)


IL の比較は少し面倒だったので、両方のプラットフォームで両方のバイナリを試してみることにしました。これをテストするためだけに、古い Windows 仮想マシンを復活させました :)

VS でコンパイルされたバイナリを Mono で実行することは問題ありません。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

さて、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がインストールされるのを(永遠に)待って、デバッグツールをインストールすること(不明)と、それに亀裂が入ること(おそらく深夜まで)だけです。また後でな

于 2011-04-24T12:49:05.083 に答える