我正在写一个小库,希望能够对数组求和。 单线程版本工作正常,但是当我添加多线程时,一切都会中断。
我使用分区器在块上分割数据,然后对单个结果中的每个部分求和。然后我回来了。但数据无效,似乎没有任何竞争条件,因为每次重启程序都会产生相同的结果。
[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;
}
如何解决?
答案 0 :(得分:2)
好的,我想我已经修好了。我发现了两个主要的错误。
通过这些更改,我得到了-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;
}