我已经写了3种不同的方法来计算整数数组的和,但是,对于第三种方法,我得到了不同的结果。
初始化:
int n = 100;
int[] mArray = new int[n];
for (int i = 0; i < mArray.Length; i++)
mArray[i] = 1;
第一:
int sum1 = mArray.Sum();
Console.WriteLine("sum1 " + sum1);
第二:
int sum2 = 0;
for (int i = 0; i < mArray.Length; i++)
sum2 += mArray[i];
Console.WriteLine("sum2 " + sum2);
第三:
int sum3 = 0;
Parallel.ForEach(mArray, item =>
{
sum3 += item;
});
Console.WriteLine("sum3 " + sum3);
很明显,这三种方法的输出如下所示:
但是,当n增加时(例如n = 30000),第三种方法会给出令人惊讶的错误结果
注意:我已经使用ConcurrentBag(一种线程安全的集合)测试了这些方法。我想,没有溢出问题。该代码已在Windows 10 x64计算机(英特尔酷睿I-7 @ 3.30ghz)上进行了测试
理解Parallel.For为什么表现不同会很有趣。
答案 0 :(得分:5)
问题是使用sum3
时多个线程访问Parallel.ForEach
。 sum3 += item;
通常涉及三个操作:
1.将sum3
的值读入临时存储区。
2.使用item
增加该存储的值;
3.将结果存储回sum3
。
当有多个线程同时执行此操作时,操作很可能会混杂在一起。例如,如果您有两个线程A和B,它们都可以从sum3
中读取相同的值,然后进行加法并存储新值。
要解决此问题,您需要保护对sum3
的访问。代码应如下所示:
object objLock = new object();
int sum3 = 0;
Parallel.ForEach(mArray, item =>
{
lock (objLock) { sum3 += item; }
});
Console.WriteLine("sum3 " + sum3);
但是,这将完全抵消并行执行的效果。
答案 1 :(得分:0)
我有尼克的解决方案,它可以解决问题,但是,使用时出现了性能问题
lock (objLock) { sum3 += item; }
直接在Parallel.ForEach中,如下图所示
幸运的是,使用.Net中正确定义的并行聚合操作解决了该问题。这是代码
object locker = new object();
double sum4= 0;
Parallel.ForEach(mArray,
() => 0.0, // Initialize the local value.
(i, state, localResult) => localResult + i, localTotal => // Body delegate which returns the new local total. // Add the local value
{
lock (locker) sum4+= localTotal;
} // to the master value.
);