多线程求和会得到不正确的结果

时间:2015-11-13 14:15:02

标签: c# .net multithreading

我正在写一个小库,希望能够对数组求和。 单线程版本工作正常,但是当我添加多线程时,一切都会中断。

我使用分区器在块上分割数据,然后对单个结果中的每个部分求和。然后我回来了。但数据无效,似乎没有任何竞争条件,因为每次重启程序都会产生相同的结果。

[Pure]
public static double Sum(this double[] source)
{
    source.IsNotNull("source");
    if (source.Length < Constants.SingleThreadExecutionThreshold)
        return Sum(source, 0, source.Length);
    double result = 0;
    object syncRoot = new object();     
    Parallel.ForEach(Partitioner.Create(0, source.Length),
        () => (double)0,
        (range, state, sum) => Sum(source, range.Item1, range.Item2),
        x =>
        {
            lock (syncRoot)
                result += x;
        });

    return result;
}

Sum(source, from, to)始终给出正确的结果。这是一个实现:

[Pure]
private static double Sum(this double[] source, int startIndex, int endIndex)
{
    double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
    checked
    {
        int i;
        for (i = startIndex; i < endIndex - Constants.Step + 1; i += Constants.Step)
        {
            sum1 += source[i];
            sum2 += source[i + 1];
            sum3 += source[i + 2];
            sum4 += source[i + 3];
        }
        if (i == source.Length)
            return ((sum1 + sum2) + (sum3 + sum4));
        if (i == source.Length - 1)
            return ((sum1 + sum2) + (sum3 + sum4) + source[i]);
        if (i == source.Length - 2)
            return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1]));
        return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1] + source[i + 2]));
    }
}

internal static class Constants
{
    public const int Step = 4;
    public const int SingleThreadExecutionThreshold = 1024;
}

如何解决?

代码示例:http://ideone.com/8sD0JL

1 个答案:

答案 0 :(得分:2)

好的,我想我已经修好了。我发现了两个主要的错误。

  1. array.Length thing
  2. 你误导了“最后”的代表。无法保证该代码能够运行
  3. 通过这些更改,我得到了-0.000576496124267578的差异,这是双倍舍入误差的预期值。

        [Pure]
    public static double Sum(this double[] source, int startIndex, int endIndex)
    {
        double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
        checked
        {
            int i;
            int j = 0;
            for (i = startIndex; i < endIndex - Constants.Step + 1; i += Constants.Step)
            {
                sum1 += source[i];
                sum2 += source[i + 1];
                sum3 += source[i + 2];
                sum4 += source[i + 3];
                j += Constants.Step;
            }
            var segmentLength = endIndex - startIndex;
    
            if (j == segmentLength)
                return ((sum1 + sum2) + (sum3 + sum4));
            if (j == segmentLength - 1)
                return ((sum1 + sum2) + (sum3 + sum4) + source[i]);
            if (j == segmentLength - 2)
                return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1]));
            return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1] + source[i + 2]));
        }
    }
    
    internal static class Constants
    {
        public const int Step = 4;
        public const int SingleThreadExecutionThreshold = 1024;
    }
    
    [Pure]
    public static double Sum(this double[] source)
    {
        if (source.Length < Constants.SingleThreadExecutionThreshold)
            return Sum(source, 0, source.Length);
        double result = 0;
        object syncRoot = new object();     
        Parallel.ForEach(Partitioner.Create(0, source.Length),
            (range) => {
    
                var x = Sum(source, range.Item1, range.Item2);
                lock (syncRoot)
                    result += x;
            });
        return result;
    }