这是一个非常奇怪的问题,我花了一天时间试图追查。我不确定这是否是一个错误,但最好能对为什么会发生这种情况有一些看法和想法。
我正在使用 xUnit (2.0) 来运行我的单元测试。xUnit 的美妙之处在于它会自动为您并行运行测试。但是,我发现的问题是,当标记为线程安全类型时,它Constructor.GetParameters
似乎不是线程安全的。ConstructorInfo
也就是说,如果两个线程Constructor.GetParameters
同时到达,则会产生两个结果,随后对该方法的调用将返回创建的第二个结果(不管调用它的线程如何)。
我创建了一些代码来演示这种意外行为(如果您想在本地下载并尝试该项目,我也将它托管在 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 ) {}
}
这两个测试类确保创建两个线程并并行运行。运行这些测试后,您应该得到如下所示的结果:
Initialized:
39053774 <---- Different!
45653674
After Initialized:
39053774 <---- Different!
45653674
45653674
45653674
(注意:我添加了“<---- 不同!”来表示意外值。您不会在测试结果中看到这一点。)
如您所见,第一次调用的结果GetParameters
返回的值与所有后续调用不同。
我在 .NET 中已经有很长一段时间了,但从未见过像这样的东西。这是预期的行为吗?是否有初始化 .NET 类型系统的首选/已知方法,以免发生这种情况?
最后,如果有人感兴趣,我在使用带有 MEF 2 的 xUnit 时遇到了这个问题,其中用作字典中的键的 ParameterInfo 不返回等于从先前保存的 value 传入的 ParameterInfo。当然,这会导致意外行为并导致同时运行时测试失败。
编辑:从答案中得到一些好的反馈后,我(希望)澄清了这个问题和场景。问题的核心是“Thead-Safe”类型的“Thread-Safety”,并更好地了解这究竟意味着什么。
回答:这个问题最终是由于几个因素造成的,其中一个是由于我对多线程场景的无知无知,在可预见的未来,我似乎永远在学习,没有尽头。我再次感谢 xUnit 的设计方式以如此有效的方式学习这一领域。
另一个问题似乎与 .NET 类型系统的初始化方式不一致。使用 TypeInfo/Type,无论哪个线程多次访问它,您都会获得相同的类型/引用/哈希码。对于 MemberInfo/MethodInfo/ParameterInfo,情况并非如此。线程访问要小心。
最后,似乎我不是唯一一个有这种困惑的人,这确实被认为是对 .NET Core 的 GitHub 存储库提交的问题的无效假设。
所以,问题解决了,主要是。我要感谢所有参与处理我在这件事上的无知的人,并帮助我学习(我发现的是)这个非常复杂的问题空间。