C# 多个并行的ForEach调用,内存载体?
我有一堆数据行,我想使用Parallel.ForEach计算每行上的一些值,如下所示C# 多个并行的ForEach调用,内存载体?,c#,multithreading,task-parallel-library,parallel.foreach,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
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;
});
}
}
类数据行
{
公共双精度A{get;内部集;}
公共双B{get;内部集;}
公共双C{get;内部集合;}
公共数据行()
{
A=双精度n;
B=双精度n;
C=双N;
}
}
班级计划
{
静态void ParallelForEachToyExample()
{
var rnd=新随机数();
var df=新列表();
对于(int i=0;i<10000000;i++)
{
var dr=new DataRow{A=rnd.NextDouble()};
添加(dr);
}
//是否需要?(I)
//Thread.MemoryBarrier();
//每个(II)的平行线
Parallel.ForEach(df,dr=>
{
B博士=2.0*A博士;
});
//是否需要?(III)
//Thread.MemoryBarrier();
//每2(IV)个并联
Parallel.ForEach(df,dr=>
{
C博士=2.0*B博士;
});
}
}
(在本例中,不需要并行化,如果需要的话,它可以全部放在一个Parallel.ForEach中。但这是一些代码的简化版本,可以这样设置)
有没有可能在这里对读取进行重新排序,以便最终得到一个数据行,其中B!=2A或C!=2B
假设第一个Parallel.ForEach(II)分配工作线程42处理数据行0。第二个Parallel.ForEach(IV)分配工作线程43处理数据行0(第一个Parallel.ForEach完成后)。由于还没有看到来自线程42的写操作,所以线程43上第0行的dr.B的读取是否有可能返回double.NaN
如果是这样,在III上插入记忆屏障是否有帮助?这会迫使第一个Parallel.ForEach的更新在第二个Parallel.ForEach启动之前对所有线程可见吗?由
Parallel.ForEach()启动的工作将在返回之前完成。在内部,ForEach()。因此,您不需要在ForEach()调用之间同步访问
对于带有ForEach()
重载的单个任务,您确实需要记住这一点,这些重载允许您访问循环状态、聚合任务结果等。例如,在这个简单的示例中,总结了1≤ x≤ 100
,传递给Parallel的localFinally
的操作。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);
var总计=0;
Parallel.For(0,101,()=>0,//{/{由于多个线程将访问同一变量“dr.B”,因此需要确保C#代码是线程安全的
尝试在每个操作周围使用“锁定”
e、 g
但是,这样做会破坏并行处理,因为每个线程都必须等待下一个线程完成
确保阅读并行处理的潜在陷阱:
简而言之..我认为您不需要明确的内存障碍..一个有根据的猜测是Parallel.ForEach的实现有某种同步来结束循环/在调用ForEach
returns之前如果您对实际代码有更好的了解,我可能会给您一个更好的答案,而不是“不,不用担心。”:)如果我说第二个并行循环(IV)的每一行中的计算取决于一些只有在第一个循环(II)之后才能知道的值,那么分离的原因可能会更清楚一些完成。假设我们需要所有行的dr.B值的中值,然后才能计算每行的dr.C值。在OP的特定示例中,使用Parallel.ForEach()
,每个ForEach()都是
call已经处理了同步,特别是确保调用产生的任何并行操作在返回之前都已完成。@jdpenix-您能提供一个参考吗(供我参考)?注意Microsoft MSDN演示了:如何:编写一个具有使用的线程局部变量的parallel.ForEach循环(finalResult)(一个单独的代码>前缀)(/CODE)确实需要考虑线程安全性,因此“代码>前缀”(/CODE)提供了重载,允许重载线程本地和终结器的规范。,我必须查看参考源来确认。我可以锁定每一个数据行。我同意99%的时间是正确的事情来避免推理低锁代码。但是,在这里,我有大量的数据行可以很好地并行处理。添加锁定会减慢我的真实世界代码。很好。我真的不需要保证互斥。每个工作线程在设计上一次只能在一个数据行上运行。我真正需要做的是确保如果AD a在第一个循环中在第一行上运行,线程B在第二个循环中运行,那么B可以在继续之前看到a的所有写入。谢谢。当我查看Parallel.ForEach再往下几层,看起来“private static ParallelLoopResult ForWorker”正在完成大部分工作。我有点难以理解,但似乎有一个调用“rootTask.Wait();"这将等待所有工作线程完成,然后再继续。但是,即使我的主线程正在等待工作线程完成,这也不能保证分布在所有其他处理器之间的工作线程在读取值时一定会看到最近的写入,是吗?这是正确的,我将编辑我的答案也许更清楚一点。由相同的ForEach()
调用生成的任务将需要注意并发性问题——这是常见的问题
private Object thisLock1 = new Object();
...
lock(thisLock1)
{
dr.C = 2.0 * dr.B;
}
...
lock(thisLock1)
{
dr.B = 2.0*dr.A;
}