[英]C# How Parallel.ForEach / Parallel.For partitioning works
[英]parallel.foreach works, but why?
谁能解释,为什么该程序返回sqrt_min的正确值?
int n = 1000000;
double[] myArr = new double[n];
for(int i = n-1 ; i>= 0; i--){ myArr[i] = (double)i;}
// sqrt_min contains minimal sqrt-value
double sqrt_min = double.MaxValue;
Parallel.ForEach(myArr, num =>
{
double sqrt = Math.Sqrt(num); // some time consuming calculation that should be parallized
if(sqrt < sqrt_min){ sqrt_min = sqrt;}
});
Console.WriteLine("minimum: "+sqrt_min);
运气很好。 有时,当您运行它时,您很幸运 ,对double的非原子读取和写入不会导致“ torn”值。 有时,您很幸运 ,当比赛发生时,非原子测试和设置恰好设置了正确的值。 不能保证该程序会产生任何特定的结果。
您的代码不安全; 它只是偶然地起作用。
如果两个线程同时运行if
,则最小值之一将被覆盖:
sqrt_min = 6
sqrt = 5
sqrt = 4
if
if
sqrt_min = 4
sqrt_min = 5
在32位系统上,您还容易受到读/写撕裂的攻击。
可以在循环中使用Interlocked.CompareExchange
使其安全。
至于为什么您的原始代码被破坏,请检查其他答案,我不再赘述。
当没有对共享状态的写访问权时,多线程是最容易的。 幸运的是,您的代码可以用这种方式编写。 在这种情况下,并行linq可能会很好,但是有时开销太大。
您可以将代码重写为:
double sqrt_min = myArr.AsParallel().Select(x=>Math.Sqrt(x)).Min();
在您的特定问题中,交换Min
和Sqrt
操作更快,这是可能的,因为Sqrt
单调增加。
double sqrt_min = Math.Sqrt(myArr.AsParallel().Min())
您的代码并没有真正起作用:我在一个循环中运行了100,000次,并且在我的8核计算机上一次失败,产生了以下输出:
minimum: 1
我缩短了运行时间,以使错误显示更快。
这是我的修改:
static void Run() {
int n = 10;
double[] myArr = new double[n];
for (int i = n - 1; i >= 0; i--) { myArr[i] = (double)i*i; }
// sqrt_min contains minimal sqrt-value
double sqrt_min = double.MaxValue;
Parallel.ForEach(myArr, num => {
double sqrt = Math.Sqrt(num); // some time consuming calculation that should be parallized
if (sqrt < sqrt_min) { sqrt_min = sqrt; }
});
if (sqrt_min > 0) {
Console.WriteLine("minimum: " + sqrt_min);
}
}
static void Main() {
for (int i = 0; i != 100000; i++ ) {
Run();
}
}
考虑到共享变量读写之间缺乏同步,这不是巧合。
正如其他人所说,这仅基于运气。 不过,OP和其他张贴者在创建比赛条件时都遇到了麻烦。 这很容易解释。 该代码生成了很多竞争条件,但是其中绝大多数(精确到99.9999%)都是无关紧要的。 最重要的是,最终结果应为0。 如果您的代码认为根5大于根6,或者根234大于根235,它仍然不会中断。 特别需要有一个竞争条件,该迭代会生成0。其中一个迭代具有另一个竞争条件的可能性非常非常高。 处理最后一项的迭代具有竞争条件的几率确实非常低。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.