使用 Parallel.For 在 C# 中锁定的意外行为
Unexpected behavior of lock in C# with Parallel.For
执行以下操作
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;
}
...
}
returns预期的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;
}
}
);
相反,我得到了完全不同的值,比如在竞争条件下。为什么?
您应该在返回迭代值时聚合部分和:
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
):
double plinqSum = Enumerable
.Range(0, (int) max)
.AsParallel()
.Sum(i => Math.Sqrt(i)); // or just .Sum(Math.Sqrt);
这个 .AsParallel().Sum()
位基本上就是您使用 Parallel.For()
构建的内容
执行以下操作
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;
}
...
}
returns预期的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;
}
}
);
相反,我得到了完全不同的值,比如在竞争条件下。为什么?
您应该在返回迭代值时聚合部分和:
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
):
double plinqSum = Enumerable
.Range(0, (int) max)
.AsParallel()
.Sum(i => Math.Sqrt(i)); // or just .Sum(Math.Sqrt);
这个 .AsParallel().Sum()
位基本上就是您使用 Parallel.For()