Parallel.For():在循环外更新变量

Ini*_*eer 35 .net c# parallel-processing .net-4.0

我只是在研究新的.NET 4.0功能.有了这个,我正在尝试使用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());
Run Code Online (Sandbox Code Playgroud)

我的猜测是线程正在尝试同时更新"sum".
有明显的方法吗?

Ade*_*ler 69

你不能这样做.sum正在您的并行线程中共享.您需要确保该sum变量一次只能被一个线程访问:

// DON'T DO THIS!
Parallel.For(0, data.Count, i =>
{
    Interlocked.Add(ref sum, data[i]);
});
Run Code Online (Sandbox Code Playgroud)

但是......这是一种反模式,因为你已经有效地序列化了循环,因为每个线程都会锁定Interlocked.Add.

您需要做的是添加小计并在最后合并它们,如下所示:

Parallel.For<int>(0, result.Count, () => 0, (i, loop, subtotal) =>
    {
        subtotal += result[i];
        return subtotal;
    },
    (x) => Interlocked.Add(ref sum, x)
);
Run Code Online (Sandbox Code Playgroud)

您可以在MSDN上找到对此的进一步讨论:http://msdn.microsoft.com/en-us/library/dd460703.aspx

PLUG:您可以在"并行编程指南"的第2章中找到更多相关信息

以下也绝对值得一读......

并行编程模式:使用.NET Framework 4理解和应用并行模式 - Stephen Toub


Blu*_*eft 17

sum += y;实际上是sum = sum + y;.由于以下竞争条件,您得到的结果不正确:

  1. Thread1读取 sum
  2. Thread2读取 sum
  3. Thread1计算sum+y1并存储结果sum
  4. Thread2计算sum+y2并存储结果sum

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


SLa*_*aks 5

你的猜测是正确的.

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

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

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

您需要使用Interlocked.Add,它作为单个原子操作执行添加.

  • 见下文.使用互锁添加的nieve方式简单地序列化你的循环. (5认同)