并行性问题

时间:2019-01-23 16:47:37

标签: c# multithreading parallel.foreach

我已经写了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);

很明显,这三种方法的输出如下所示:

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

注意:我已经使用ConcurrentBag(一种线程安全的集合)测试了这些方法。我想,没有溢出问题。该代码已在Windows 10 x64计算机(英特尔酷睿I-7 @ 3.30ghz)上进行了测试

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

2 个答案:

答案 0 :(得分:5)

问题是使用sum3时多个线程访问Parallel.ForEachsum3 += 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);

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

答案 1 :(得分:0)

我有尼克的解决方案,它可以解决问题,但是,使用时出现了性能问题 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.
                        );