5

これは、私が追跡しようとして一日を費やした非常に奇妙な問題です。これがバグかどうかはわかりませんが、なぜこれが起こっているのかについての見解と考えを得ることができれば幸いです.

xUnit (2.0) を使用して単体テストを実行しています。xUnit の優れた点は、テストを自動的に並行して実行することです。しかし、私が見つけた問題は、 がスレッド セーフ タイプであるとマークされているConstructor.GetParameters場合、スレッド セーフではないように見えることです。ConstructorInfoつまり、2 つのスレッドがConstructor.GetParameters同時に到達した場合、2 つの結果が生成され、このメソッドへの後続の呼び出しは、作成された 2 番目の結果を返します (呼び出し元のスレッドに関係なく)。

この予期しない動作を示すコードをいくつか作成しました (プロジェクトをローカルにダウンロードして試してみたい場合は、GitHub でもホストしています)。

コードは次のとおりです。

public class OneClass
{
    readonly ITestOutputHelper output;

    public OneClass( ITestOutputHelper output )
    {
        this.output = output;
    }

    [Fact]
    public void OutputHashCode()
    {
        Support.Add( typeof(SampleObject).GetTypeInfo() );
        output.WriteLine( "Initialized:" );
        Support.Output( output );

        Support.Add( typeof(SampleObject).GetTypeInfo() );
        output.WriteLine( "After Initialized:" );
        Support.Output( output );
    }
}

public class AnotherClass
{
    readonly ITestOutputHelper output;

    public AnotherClass( ITestOutputHelper output )
    {
        this.output = output;
    }

    [Fact]
    public void OutputHashCode()
    {
        Support.Add( typeof(SampleObject).GetTypeInfo() );
        output.WriteLine( "Initialized:" );
        Support.Output( output );

        Support.Add( typeof(SampleObject).GetTypeInfo() );
        output.WriteLine( "After Initialized:" );
        Support.Output( output );
    }
}

public static class Support
{
    readonly static ICollection<int> Numbers = new List<int>();

    public static void Add( TypeInfo info )
    {
        var code = info.DeclaredConstructors.Single().GetParameters().Single().GetHashCode();
        Numbers.Add( code );
    }

    public static void Output( ITestOutputHelper output )
    {
        foreach ( var number in Numbers.ToArray() )
        {
            output.WriteLine( number.ToString() );
        }
    }
}

public class SampleObject
{
    public SampleObject( object parameter ) {}
}

2 つのテスト クラスにより、2 つのスレッドが作成され、並行して実行されることが保証されます。これらのテストを実行すると、次のような結果が得られるはずです。

Initialized:
39053774 <---- Different!
45653674
After Initialized:
39053774 <---- Different!
45653674
45653674
45653674

(注: 予期しない値を示すために「<---- 違います!」を追加しました。これはテスト結果には表示されません。)

ご覧のとおり、 への最初の呼び出しの結果は、GetParameters後続のすべての呼び出しとは異なる値を返します。

私はかなり長い間 .NET に興味を持っていましたが、このようなものは見たことがありません。これは予想される動作ですか?これが起こらないように.NET型システムを初期化するための推奨/既知の方法はありますか?

最後に、誰かが興味を持っている場合は、MEF 2 で xUnit を使用しているときにこの問題に遭遇しました。この問題では、辞書のキーとして使用されている ParameterInfo が、以前に保存された値から渡された ParameterInfo と同じように返されません。もちろん、これにより予期しない動作が発生し、同時に実行するとテストが失敗します。

編集:回答からの良いフィードバックの後、私は(うまくいけば)この質問とシナリオを明確にしました。問題の核心は、「スレッド セーフ」タイプの「スレッド セーフ」であり、これが正確に何を意味するかについての知識を深めることです。

ANSWER:この問題は、いくつかの要因によるものでした。そのうちの 1 つは、マルチスレッド シナリオに対する私の終わりのない無知によるものです。この領域を非常に効果的な方法で学習できるように設計されている xUnit に改めて感謝しています。

もう 1 つの問題は、.NET 型システムの初期化方法との不一致のようです。TypeInfo/Type を使用すると、どのスレッドが何度アクセスしても、同じ型/参照/ハッシュコードを取得できます。MemberInfo/MethodInfo/ParameterInfo の場合、これは当てはまりません。スレッド アクセスに注意してください。

最後に、この混乱を抱えているのは私だけではないようです。これは実際、.NET Core の GitHub リポジトリに提出された問題に関する無効な仮定として認識されています

それで、問題はほとんど解決しました。この問題に関する私の無知に対処し、この非常に複雑な問題空間を (私が発見しているのは) 学習するのを手伝ってくれた関係者全員に感謝の意を表したいと思います。

4

2 に答える 2

6

これは、最初の呼び出しで 1 つのインスタンスになり、その後のすべての呼び出しで別のインスタンスになります。

OK、それは大丈夫です。少し奇妙ですが、このメソッドは毎回同じインスタンスを返すように文書化されていません。

したがって、最初の呼び出しで 1 つのスレッドが 1 つのバージョンを取得し、その後、各スレッドが別のバージョンを取得します (後続の各呼び出しでインスタンスは変更されません。

繰り返しますが、奇妙ですが、完全に合法です。

これは予想される動作ですか?

まあ、あなたの実験の前に私はそれを期待していなかっただろう. しかし、あなたの実験の後、はい、その行動が続くと思います.

これが起こらないように.NET型システムを初期化するための推奨/既知の方法はありますか?

私の知る限りではありません。

その最初の呼び出しを使用してキーを保存している場合、はい、それは問題です。

そうすれば、それをやめるべきだという証拠が得られます。そうするときに痛い場合は、しないでください。

ParameterInfo 参照は、それが使用されているスレッドやアクセス回数に関係なく、常に同じ ParameterInfo 参照を表す必要があります。

これは、機能がどのように設計されるべきかについての道徳的声明です。それはどのよう設計されたかではなく、明らかにどのように実装されたかではありません。設計が悪いという主張は確かにできます。

ドキュメントがこれを保証/指定していないという点でもリッパート氏は正しいですが、これは常にこの時点までのこの動作に対する私の期待と経験でした。

過去のパフォーマンスは将来の結果を保証するものではありません。あなたの経験は今まで十分に変化していませんでした。マルチスレッドには、人々の期待を混乱させる方法があります。何かが静止していない限り、記憶が常に変化している世界は、何かが変化するまで同じであるという私たちの通常のモードに反しています.

于 2016-03-13T22:40:42.997 に答える