我有一个C#单元测试(实际上是一个集成测试),它测试包含Parallel.ForEach
循环的代码块。当此代码在调试模式下,本地或已部署的环境中运行时,并行性将按预期工作(约20秒内完成)。但是,当我的集成测试命中该代码段时,它们最终会超时(超过4分钟)。我的猜测是,在单元测试运行时,某种程度上并行特性被关闭了。我知道这不是硬件问题,因为同一台机器可以在调试时执行并行性,而在运行单元测试时则很慢。但是,我不知道单元测试的设置或配置会导致什么。
请注意,并行运行测试不是问题-由于其他限制,我们必须串行运行测试。这是在单个测试中,如果Parallel.ForEach
的性能在调试(通过IIS localhost)或运行单元测试的同时运行,则在同一台计算机,同一代码块上的性能会有很大不同。
我们的单元测试框架是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;
}
同样,我可以在同一台计算机上在Web应用程序中运行此代码,并且该代码会在20秒内针对该请求执行。在同一台计算机上以相同的代码在单元测试中执行相同的请求,这需要4分钟。关于可能会使其变慢的原因,我遇到了很多死胡同,但我只是不知道。
Parallel.ForEach
循环确实可以并行执行(看起来在我使用的机器上一次大约有10个线程)任何对调查事项的帮助或建议将不胜感激。