并行性问题
A parallelism issue
我已经编写了 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);
显然,这 3 种方法给出了相同的输出,如下所示:
然而,当 n 增加时(例如,n = 30000),第三种方法给出了令人惊讶的错误结果
注意:我已经使用线程安全集合 ConcurrentBag 测试了这些方法。我想,没有溢出问题。代码在 windows 10 x64 计算机(Intel 核心 I-7 @ 3.30ghz)
上测试
理解为什么 Parallel.For 行为不同会很有趣。
问题是当您使用 Parallel.ForEach
时,sum3
被多个线程访问。 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);
但是,这将完全抵消并行执行的效果。
我有 Nick 的解决方案,它解决了问题,但是,在使用时出现了性能问题
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.
);
我已经编写了 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);
显然,这 3 种方法给出了相同的输出,如下所示:
然而,当 n 增加时(例如,n = 30000),第三种方法给出了令人惊讶的错误结果
注意:我已经使用线程安全集合 ConcurrentBag 测试了这些方法。我想,没有溢出问题。代码在 windows 10 x64 计算机(Intel 核心 I-7 @ 3.30ghz)
上测试理解为什么 Parallel.For 行为不同会很有趣。
问题是当您使用 Parallel.ForEach
时,sum3
被多个线程访问。 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);
但是,这将完全抵消并行执行的效果。
我有 Nick 的解决方案,它解决了问题,但是,在使用时出现了性能问题
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.
);