これは、私が追跡しようとして一日を費やした非常に奇妙な問題です。これがバグかどうかはわかりませんが、なぜこれが起こっているのかについての見解と考えを得ることができれば幸いです.
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 リポジトリに提出された問題に関する無効な仮定として認識されています。
それで、問題はほとんど解決しました。この問題に関する私の無知に対処し、この非常に複雑な問題空間を (私が発見しているのは) 学習するのを手伝ってくれた関係者全員に感謝の意を表したいと思います。