我有一堆数据行,我想使用
Parallel.ForEach
来计算每行上的一些值,如下所示...
class DataRow
{
public double A { get; internal set; }
public double B { get; internal set; }
public double C { get; internal set; }
public DataRow()
{
A = double.NaN;
B = double.NaN;
C = double.NaN;
}
}
class Program
{
static void ParallelForEachToyExample()
{
var rnd = new Random();
var df = new List<DataRow>();
for (int i = 0; i < 10000000; i++)
{
var dr = new DataRow {A = rnd.NextDouble()};
df.Add(dr);
}
// Ever Needed? (I)
//Thread.MemoryBarrier();
// Parallel For Each (II)
Parallel.ForEach(df, dr =>
{
dr.B = 2.0 * dr.A;
});
// Ever Needed? (III)
//Thread.MemoryBarrier();
// Parallel For Each 2 (IV)
Parallel.ForEach(df, dr =>
{
dr.C = 2.0 * dr.B;
});
}
}
(在这个例子中,不需要并行化,如果有的话,它可以全部进入一个
Parallel.ForEach
。但这意味着这是一些代码的简化版本,这样设置是有意义的)。
是否可以在此处重新排序读取,以便我最终得到一个数据行,其中
B != 2A
或 C != 2B
?
假设第一个
Parallel.ForEach
(II) 分配工作线程 42 处理数据行 0。第二个 Parallel.ForEach
(IV) 分配工作线程 43 处理数据行 0(第一个 Parallel.ForEach
完成后) )。线程 43 上第 0 行的读取是否有可能返回 dr.B
,因为它还没有看到线程 42 的写入?如果是这样,在 III 处插入内存屏障有帮助吗?这是否会强制在第二个 double.NaN
开始之前,第一个
Parallel.ForEach
的更新对所有线程可见?Parallel.ForEach
开始的工作将在其返回之前完成。在内部,
Parallel.ForEach()
为每次迭代生成一个ForEach()
,并在每次迭代上调用Task
。因此,您不需要在 Wait()
调用之间同步访问。您do
需要牢记对于具有ForEach()
重载的各个任务,这些重载允许您访问循环状态、聚合任务结果等。例如,在这个总结了
ForEach()
的简单示例中,1 ≤ x ≤ 100
传递给Action
的localFinally
必须要关心同步问题,Parallel.For()
在您的示例中,无需在
var total = 0;
Parallel.For(0, 101, () => 0, // <-- localInit
(i, state, localTotal) => { // <-- body
localTotal += i;
return localTotal;
}, localTotal => { <-- localFinally
Interlocked.Add(ref total, localTotal); // Note the use of an `Interlocked` static method
});
// Work of previous `For()` call is guaranteed to be done here
Console.WriteLine(total);
调用之间插入内存屏障。具体来说,循环
ForEach()
可以依赖于 IV
完成的结果,并且 II
已经为您插入了 Parallel.ForEach()
。 片段源自:并行框架和避免错误共享