Parallel.For():更新循环外部的variables

我只是在寻找新的.NET 4.0function。 与此同时,我正在尝试使用Parallel.Forfor(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是在你的并行线程中共享的。 您需要确保sumvariables一次只能被一个线程访问:

 // 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. 线程1读取sum
  2. 线程2读取sum
  3. Thread1计算sum+y1 ,并将结果存储在sum
  4. Thread2计算sum+y2 ,并将结果存储在sum

sum现在等于sum+y2 ,而不是sum+y1+y2

你的猜测是正确的。

在编写sum += y ,运行时会执行以下操作:

  1. 将字段读入堆栈
  2. y添加到堆栈
  3. 将结果写回现场

如果两个线程同时读取该字段,则第一个线程所做的更改将被第二个线程覆盖。

您需要使用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; } ); 

我们会怎样做 ? 我猜测,必须使用数组!