使用ConcurrentBag与一个简单数组在并行for
本文关键字:数组 简单 并行 for 一个 ConcurrentBag 使用 | 更新日期: 2023-09-27 18:11:09
这是一个假设的问题/用例,基于在这个特定用例中使用(在这种情况下)ConcurrentBag而不是Parallel for循环中的一个简单数组的好处。
该场景基于使用一个通用的Pipeline模式来分析从1到总结果的数字,并基于某个Pipeline操作的输出不为空来存储结果。
结果列表的实际顺序很重要,因此使用简单列表(字符串类型)。Add将导致基于每个线程决定何时返回结果的奇怪。
我有以下工作代码:
public IList<string> Execute(int total)
{
var items = new ConcurrentBag<AnalyzerResult>();
Parallel.ForEach(Iterate(1, (total + 1)), d =>
{
foreach (IOperation<T> operation in operations)
{
var result = operation.Execute(d);
if (result != null)
{
items.Add(new AnalyzerResult(d, result));
break;
}
}
});
return items.OrderBy(o=>o.SortOrder).Select(d => d.Result).ToList();
}
AnalyzerResult是一个简单的不可变类,代码只会将新项推送到包中(因此理论上没有items列表中的内容被更改的危险)。
基于此,是否一个简单的数组是足够的(并包含较少的代码噪音)?或者使用并发类型会被认为是更好的实践/性能更高吗?例如:
public IList<string> Execute(int total)
{
var items = new string[total];
Parallel.ForEach(Iterate(1, (total + 1)), d =>
{
foreach (IOperation<T> operation in operations)
{
var result = operation.Execute(d);
if (result != null)
{
items[(d - 1)] = result;
break;
}
}
});
return items.ToList();
}
注意:这不是并发性问题,两个方法都是合法的,并且产生期望的结果没有问题。
我最初的回答是"你需要并发保护",但后来重读了你问题的第二部分。
这看起来应该可以工作,因为您不会尝试从两个不同的线程写入内存中的相同位置。因此,消除锁和线程亲和性(ConcurrentBag
提供的)应该可以显著提高性能。
真正的问题是-增加了多少,这是一个必要的增加(需要配置文件),你是否会在将来改变这个设置,这样你就需要并发保护。
就这样,它应该很好,而且可读性很好。您可能想要注释这段代码,以说明为什么这样做,以确保有人不会随意浏览它并认为"并发问题"(就像我刚才做的那样)并"修复"它。