使用 Parallel.For 在 C# 中锁定的意外行为

Unexpected behavior of lock in C# with Parallel.For

执行以下操作

static object aggLock = new object();
static long max = 10000000;

static void Main(string[] args)
{
        double totalSumSeq = 0;
        double totalSumLock = 0;

        // Seq
        for (int i = 0; i < max; i++)
        {
            double y = Math.Sqrt(i);
            totalSumSeq += y;
        }

        ...
}

returns预期的21,081,849,486.4393.

正在使用

        // Parallel.For(from, to, init, body, finally);
        Parallel.For(0, max, () => 0.0, (i, pls, y) => // (LoopVariable, ParallelLoopState, ThreadLocalVariable)
        {
            y = Math.Sqrt(i);
            return y;
        },
        partSum =>
        {
            lock (aggLock)
            {
                totalSumLock += partSum;
            }
        }
        );

相反,我得到了完全不同的值,比如在竞争条件下。为什么?

您应该在返回迭代值时聚合部分和:

Parallel.For(0, max, () => 0.0, (i, pls, y) => 
 {
    //y = Math.Sqrt(i);
    int r = y + Math.Sqrt(i);  // a + to  fix it
    return r;
 }, ...

y 使用 () => 0.0 初始化为 0.0,并在分区末尾重新出现为 partSum。但是您只使用了分区的最后一个值。

使用 PLinq 的替代方案(但 Range() 不接受最大值 long):

        double plinqSum = Enumerable
            .Range(0, (int) max)
            .AsParallel()
            .Sum(i => Math.Sqrt(i));  // or just  .Sum(Math.Sqrt);

这个 .AsParallel().Sum() 位基本上就是您使用 Parallel.For()

构建的内容