多个并行.对于每个调用,MemoryBarrier

本文关键字:调用 MemoryBarrier 于每个 并行 | 更新日期: 2023-09-27 17:49:14

我有一堆数据行,我想使用并行。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的数据行?

说出第一个平行。ForEach (II)将工作线程42分配给数据行0。第二个平行线。ForEach (IV)将工作线程43分配给数据行0上的工作(只要第一个并行)。ForEach完成)。是否有可能在线程43上读取dr.B的第0行返回double。NaN,因为它还没有看到线程42的写入吗?

如果是这样,在III中插入内存屏障是否有帮助?这将强制从第一个并行更新。ForEach必须在第二个Parallel之前对所有线程可见。ForEach开始吗?

多个并行.对于每个调用,MemoryBarrier

Parallel.ForEach()启动的工作将在其返回之前完成。在内部,ForEach()为每次迭代生成一个Task,并在每次迭代上调用Wait()。因此,您不需要在ForEach()调用之间同步访问。

确实需要记住,对于具有ForEach()过载的单个任务,允许您访问循环状态,从任务中聚合结果等。例如,在这个总结了1 ≤ x ≤ 100的简单示例中,传递给Parallel.For()localFinallyAction必须关注同步问题,

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()已经为您插入了III

代码段来自:Parallel Framework和避免错误共享

由于不止一个线程将访问同一个变量"dr.B",因此您需要确保您的c#代码是线程安全的。

尝试在每个操作中使用"lock"https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx

private Object thisLock1 = new Object();
...
lock(thisLock1)
{
    dr.C = 2.0 * dr.B;
}
...
lock(thisLock1)
{
    dr.B = 2.0*dr.A;
}

然而,这样做会破坏并行处理。因为每个线程都必须等待下一个线程完成。

确保阅读并行处理的潜在缺陷:https://msdn.microsoft.com/en-us/library/dd997403%28v=vs.110%29.aspx