Parallel.For():更新循环外部的variables
我只是在寻找新的.NET 4.0function。 与此同时,我正在尝试使用Parallel.For
和for(x;x;x)
循环的正常计算。
但是,我在50%的时间里得到了不同的结果。
long sum = 0; Parallel.For(1, 10000, y => { sum += y; } ); Console.WriteLine(sum.ToString()); sum = 0; for (int y = 1; y < 10000; y++) { sum += y; } Console.WriteLine(sum.ToString());
我的猜测是线程正试图同时更新“sum”。
有没有一个明显的方法呢?
你不能这样做。 sum
是在你的并行线程中共享的。 您需要确保sum
variables一次只能被一个线程访问:
// DON'T DO THIS! Parallel.For(0, data.Count, i => { Interlocked.Add(ref sum, data[i]); });
但是…这是一个反模式,因为你已经有效地序列化了循环,因为每个线程都会locking在Interlocked.Add
。
你需要做的是添加小计,并像这样结束它们:
Parallel.For<int>(0, result.Count, () => 0, (i, loop, subtotal) => { subtotal += result[i]; return subtotal; }, (x) => Interlocked.Add(ref sum, x) );
你可以在MSDN上find更多的讨论: http : //msdn.microsoft.com/en-us/library/dd460703.aspx
PLUG:有关并行编程指南的第2章,您可以find更多信息
以下也绝对值得一读…
并行编程模式:使用.NET Framework 4理解和应用并行模式 – Stephen Toub
sum += y;
实际上是sum = sum + y;
。 由于以下竞赛条件,您将得到不正确的结果:
- 线程1读取
sum
- 线程2读取
sum
- Thread1计算
sum+y1
,并将结果存储在sum
- Thread2计算
sum+y2
,并将结果存储在sum
sum
现在等于sum+y2
,而不是sum+y1+y2
。
你的猜测是正确的。
在编写sum += y
,运行时会执行以下操作:
- 将字段读入堆栈
- 将
y
添加到堆栈 - 将结果写回现场
如果两个线程同时读取该字段,则第一个线程所做的更改将被第二个线程覆盖。
您需要使用Interlocked.Add
,它将执行添加作为单个primefaces操作。
增加长度不是primefaces操作。
我认为区分这个循环不能被划分为并行是很重要的,因为如上所述,循环的每个迭代都依赖于先验。 parallel for被devise用于显式并行任务,比如像素缩放等,因为循环的每次迭代都不能在其迭代之外具有数据依赖性。
Parallel.For(0, input.length, x => { output[x] = input[x] * scalingFactor; });
上面的代码示例允许轻松进行并行分区。 然而,一个警告的话,并行会带来一个代价,即使我上面用作循环的循环也是太简单了,以至于不能平行执行,因为设置时间比通过并行保存的时间要长。
似乎没有人提到的重要一点:对于数据并行操作(如OP),使用PLINQ而不是Parallel
类通常更好(在效率和简单性方面)。 OP的代码实际上并不重要:
long sum = Enumerable.Range(1, 10000).AsParallel().Sum();
上面的代码片段使用了ParallelEnumerable.Sum
方法,尽pipe也可以使用Aggregate
来获得更多的一般场景。 有关这些方法的说明,请参阅并行循环章节。
如果在这个代码中有两个参数。 例如
long sum1 = 0; long sum2 = 0; Parallel.For(1, 10000, y => { sum1 += y; sum2=sum1*y; } );
我们会怎样做 ? 我猜测,必须使用数组!