多个Parallel.ForEach调用,MemoryBarrier?

时间:2015-05-15 03:19:22

标签: c# multithreading task-parallel-library parallel.foreach

我有一堆数据行,我想使用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(一旦第一个并行。 ForEach完成)。是否有可能在线程43上读取第0行的dr.B返回double.NaN,因为它还没有看到来自线程42的写入?

如果是这样,在III处插入内存屏障是否有帮助?在第二个Parallel.ForEach启动之前,这会强制第一个Parallel.ForEach的更新对所有线程可见吗?

2 个答案:

答案 0 :(得分:4)

Parallel.ForEach()开始的工作将在返回之前完成。在内部,ForEach()为每次迭代生成Task,并在每个迭代上调用Wait()。因此,您无需在ForEach()次来电之间同步访问权限。

需要记住那些ForEach()重载的个别任务,这些重载允许你访问循环状态,聚合任务的结果等。总结1 ≤ x ≤ 100,传递给Action localFinally的{​​{1}}必须关注同步问题,

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()

摘录自:Parallel Framework and avoiding false sharing

答案 1 :(得分:0)

由于多个线程将访问同一个变量“dr.B”,因此您需要确保C#代码是线程安全的。

尝试围绕每个操作使用“锁定” https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx

e.g。

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

相关问题