c#中使用Parallel.For的锁的意外行为

本文关键字:意外 For Parallel | 更新日期: 2023-09-27 18:02:31

执行以下操作

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;
        }
        ...
}

返回预期的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;
            }
        }
        );
相反,我得到完全不同的值,就像在竞态条件中一样。为什么?

c#中使用Parallel.For的锁的意外行为

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

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作为max):

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

这个.AsParallel().Sum()位实际上就是你用Parallel.For()构建的