并行性问题

A parallelism issue

我已经编写了 3 种不同的方法来计算整数数组的总和,但是,对于第三种方法,我得到了不同的结果。

初始化:

        int n = 100;
        int[] mArray = new int[n];
        for (int i = 0; i < mArray.Length; i++)          
            mArray[i] = 1;

第一个:

        int sum1 = mArray.Sum();
        Console.WriteLine("sum1 " + sum1);

第二个:

        int sum2 = 0;
        for (int i = 0; i < mArray.Length; i++)
            sum2 += mArray[i];
        Console.WriteLine("sum2 " + sum2);

第三名:

        int sum3 = 0;
        Parallel.ForEach(mArray, item =>
        {
            sum3 += item;
        });
        Console.WriteLine("sum3 " + sum3);

显然,这 3 种方法给出了相同的输出,如下所示:

然而,当 n 增加时(例如,n = 30000),第三种方法给出了令人惊讶的错误结果

注意:我已经使用线程安全集合 ConcurrentBag 测试了这些方法。我想,没有溢出问题。代码在 windows 10 x64 计算机(Intel 核心 I-7 @ 3.30ghz)

上测试

理解为什么 Parallel.For 行为不同会很有趣。

问题是当您使用 Parallel.ForEach 时,sum3 被多个线程访问。 sum3 += item; 通常涉及三个操作: 1. 将sum3的值读入临时存储区。 2. 将该存储的值增加 item; 3. 将结果存回sum3.

当多个线程并发执行此操作时,很可能会混合操作。例如,如果您有两个线程 A 和 B,它们都可能从 sum3 读取相同的值,然后进行相加并将新值存储回去。

要解决这个问题,您需要保护您对 sum3 的访问。代码应如下所示:

 object objLock = new object();
 int sum3 = 0;
 Parallel.ForEach(mArray, item =>
 {
     lock (objLock) { sum3 += item; }
 });
 Console.WriteLine("sum3 " + sum3);

但是,这将完全抵消并行执行的效果。

我有 Nick 的解决方案,它解决了问题,但是,在使用时出现了性能问题 lock (objLock) { sum3 += item; }直接在Parallel.ForEach,如下图

幸运的是,使用 .Net 中正确定义的并行聚合操作解决了这个问题。这是代码

        object locker = new object();
        double sum4= 0;
        Parallel.ForEach(mArray,
                        () => 0.0,                 // Initialize the local value.
                        (i, state, localResult) => localResult + i, localTotal =>   // Body delegate which returns the new local total.                                                                                                                                           // Add the local value
                            {
                                lock (locker) sum4+= localTotal;
                            }    // to the master value.
                        );