C# 单元测试不';t似乎不允许parallel.foreach并行执行
我有一个C#单元测试(实际上更像是一个集成测试),它测试一个包含C# 单元测试不';t似乎不允许parallel.foreach并行执行,c#,multithreading,unit-testing,parallel-processing,xunit.net,C#,Multithreading,Unit Testing,Parallel Processing,Xunit.net,我有一个C#单元测试(实际上更像是一个集成测试),它测试一个包含Parallel.ForEach循环的代码块。当此代码在调试模式下、本地或部署的环境中运行时,并行性将按预期工作(大约20秒后完成)。然而,当我的集成测试碰到那段代码时,它们最终会超时(耗时超过4分钟)。我的猜测是,当单元测试运行时,并行性不知何故被关闭了。我知道这不是硬件情况,因为同一台机器在调试时可以执行并行,而在运行单元测试时速度较慢。然而,我不知道单元测试的什么设置或配置会导致这种情况 请注意,这不是并行运行测试的问题-由于
Parallel.ForEach
循环的代码块。当此代码在调试模式下、本地或部署的环境中运行时,并行性将按预期工作(大约20秒后完成)。然而,当我的集成测试碰到那段代码时,它们最终会超时(耗时超过4分钟)。我的猜测是,当单元测试运行时,并行性不知何故被关闭了。我知道这不是硬件情况,因为同一台机器在调试时可以执行并行,而在运行单元测试时速度较慢。然而,我不知道单元测试的什么设置或配置会导致这种情况
请注意,这不是并行运行测试的问题-由于其他限制,我们必须以串行方式运行测试。这是在单个测试中,并行的性能。如果在调试(通过IIS localhost)或运行单元测试时运行相同的代码块,则在同一台机器上的ForEach
的性能会有很大的不同
我们的单元测试框架是xUnit。我们使用TypeMock对静态调用进行大量模拟,但是我们没有配置任何应该禁止线程的东西。单元测试显示池中有32K+个线程可用。我可以有一个单独的单元测试,显示它可以并行运行
单元测试
[Fact]
public void AssertManyAssetsEnumerated()
{
var args = new EnumerateArgs();
args.FirstIndex = 0;
args.LastIndex = 22263;
args.FileName = LARGE_INDEX_ASSET_PREFIX;
args.FullLibrarySearch = true;
var stopwatch = new Stopwatch();
var translators = new FilenameFieldTranslator();
stopwatch.Restart();
var assets = Services.AssetService.Enumerate(ScenarioBuilder.MasterAdminUser, args, translators);
stopwatch.Stop();
Assert.Equal(22263, assets.Count);
var assetIDs = assets.Select(a => a.ID);
Assert.Equal(assetIDs.Distinct(), assetIDs);
var assetFileNames = assets.Select(a => a.FileName);
Assert.Equal(assetFileNames.Distinct(), assetFileNames);
Assert.Equal(PrependZerosAndCreateFileName(lower + 1), assets[0].FileName);
Assert.Equal(PrependZerosAndCreateFileName(realUpper), assets.Last().FileName);
//All asserts up to this point pass, only this time assertion fails
var start = new TimeSpan(0, 0, 0);
var maxTimespan = new TimeSpan(0, 0, 120);
Assert.InRange(stopwatch.Elapsed, start, maxTimespan);
}
循环
public SearchResult RunSearch(IUser user, AssetCollection collection, Aggregations aggregations, FieldTranslators translators)
{
if (!collection.IsLargeRequest)
{
return RunSingleSearch(user, collection, aggregations, translators);
}
var subquerySize = 1000;
var subqueryCollections = new List<AssetCollection>();
for (var i = collection.FirstIndex; i < collection.LastIndex; i += subquerySize)
{
var subqueryCollection = Clone(collection);
subqueryCollection.FirstIndex = i;
subqueryCollection.LastIndex = i + subquerySize;
subqueryCollections.Add(subqueryCollection);
}
var results = new List<SearchResult>();
var resultLock = new object();
Parallel.ForEach(subqueryCollections, c =>
{
var result = RunSingleSearch(user, c, aggregations, translators);
lock (resultLock)
{
results.Add(result);
}
});
var overallResult = CombineResults(collection, results);
return overallResult;
}
public SearchResult RunSearch(IUser用户、AssetCollection集合、聚合、字段转换器)
{
如果(!collection.IsLargeRequest)
{
返回RunSingleSearch(用户、集合、聚合、转换器);
}
var subquerySize=1000;
var subqueryCollections=新列表();
for(var i=collection.FirstIndex;i
{
var result=RunSingleSearch(用户、c、聚合、转换器);
锁(结果锁)
{
结果。添加(结果);
}
});
var overallResult=组合结果(集合、结果);
返回总体结果;
}
同样,在同一台机器上,我可以在web应用程序中运行此代码,并在20秒内执行该请求。在同一台机器上,用相同的代码在单元测试中执行相同的请求,需要4分钟。我已经陷入了很多死胡同关于什么可能会使它变慢,但我只是不知道
- 我们在xUnit测试套件中禁用了并行执行,因为TypeMock隔离器不喜欢它(至少我们使用它的方式)。我尝试再次启用它,但它没有解决单元测试中的性能问题
- 如前所述,我们使用TypeMock隔离器来模拟某些静态调用。我们没有做任何(我看到的)会限制/改变线程和/或任务的并行执行方式的事情
- 单元测试中的线程池有32K+个可用线程
- 单独的单元测试可以确认不同的
循环确实可以并行执行(在我一直使用的机器上,一次大约有10个线程)并行。ForEach
- 我们正在Visual Studio 2017中运行单元测试,使用最新版本的xUnit Visual Studio runner(2.4.0)
任何需要调查的帮助或建议都将不胜感激。我建议使用xUnit编写单元测试,并且只编写单元测试,而不要使用xUnit编写集成测试。将集成测试与单元测试混合在一起不会有好的结果。它们应该分开存放。集成测试和单元测试有非常不同的特点,例如:集成测试几乎总是只能串行运行,单元测试应该总是能够并行运行。@Eljay我当然同意。然而,这是一个遗留项目,有许多缺点。我们正在努力在我们的解决方案中使用真正的单元测试,并将它们与集成分离开来——但目前,这不是一个选项。@ODawgG——由于列表本身不是线程安全的,锁确保所有集合都正确地添加到列表中。例如,如果我的请求大小为1M,并且它必须进行1000个子查询,那么有时会有1到2个结果由于线程冲突而无法添加。