多次 Parallel.ForEach 调用,MemoryBarrier?
Multiple Parallel.ForEach calls, MemoryBarrier?
我有一堆数据行,我想使用 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 returns double.NaN 上读取第 0 行的 dr.B 因为它还没有看到来自线程 42 的写入?
如果是这样,在 III 处插入内存屏障是否有帮助?这会强制第一个 Parallel.ForEach 的更新在第二个 Parallel.ForEach 开始之前对所有线程可见吗?
由 Parallel.ForEach()
开始的工作将在它 returns 之前完成。在内部,ForEach()
为每次迭代生成一个 Task
,并在每次迭代时调用 Wait()
。因此,您不需要在 ForEach()
个调用之间同步访问。
你 do 需要记住这一点,对于具有 ForEach()
重载的个别任务,允许你访问循环状态,聚合任务结果等。例如在总结 1 ≤ x ≤ 100
的这个简单示例中,传递给 Parallel.For()
的 localFinally
的 Action
必须关注同步问题,
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
。
摘自:
由于不止一个线程将访问同一个变量 "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
我有一堆数据行,我想使用 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 returns double.NaN 上读取第 0 行的 dr.B 因为它还没有看到来自线程 42 的写入?
如果是这样,在 III 处插入内存屏障是否有帮助?这会强制第一个 Parallel.ForEach 的更新在第二个 Parallel.ForEach 开始之前对所有线程可见吗?
由 Parallel.ForEach()
开始的工作将在它 returns 之前完成。在内部,ForEach()
为每次迭代生成一个 Task
,并在每次迭代时调用 Wait()
。因此,您不需要在 ForEach()
个调用之间同步访问。
你 do 需要记住这一点,对于具有 ForEach()
重载的个别任务,允许你访问循环状态,聚合任务结果等。例如在总结 1 ≤ x ≤ 100
的这个简单示例中,传递给 Parallel.For()
的 localFinally
的 Action
必须关注同步问题,
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
。
摘自:
由于不止一个线程将访问同一个变量 "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