7

谁能解释一下,为什么这个程序会返回正确的 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);
4

5 回答 5

13

它完全靠运气。有时当您运行它时,您很幸运,对双精度的非原子读取和写入不会导致“撕裂”值。有时你很幸运,非原子测试和设置恰好在比赛发生时设置了正确的值。无法保证该程序会产生任何特定结果。

于 2012-02-06T19:49:53.423 回答
5

您的代码不安全;它只是巧合。

如果两个线程if同时运行,最小值之一将被覆盖:

  • sqrt_min = 6
  • 线程 A:sqrt = 5
  • 线程 B:sqrt = 4
  • 线程 A 进入if
  • 线程 B 进入if
  • 线程 B 分配sqrt_min = 4
  • 线程 A 分配sqrt_min = 5

在 32 位系统上,您也容易受到读/写撕裂的影响。

Interlocked.CompareExchange在循环中使用它是可能的。

于 2012-02-06T19:46:50.760 回答
4

对于为什么您的原始代码被破坏检查其他答案,我不会重复。

当对共享状态没有写访问权限时,多线程是最简单的。幸运的是,您的代码可以这样编写。在这种情况下,并行 linq 可能会很好,但有时开销太大。

您可以将代码重写为:

double sqrt_min = myArr.AsParallel().Select(x=>Math.Sqrt(x)).Min();

Min在您的特定问题中,交换and操作会更快Sqrt,这是可能的,因为Sqrt它是单调递增的。

double sqrt_min = Math.Sqrt(myArr.AsParallel().Min())
于 2012-02-06T20:51:10.370 回答
3

您的代码并没有真正起作用:我在循环中运行了 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();
    }
}

考虑到共享变量的读写缺乏同步,这不是巧合。

于 2012-02-06T19:55:02.223 回答
2

正如其他人所说,这只适用于剪切运气。不过,OP 和其他海报在实际创建比赛条件时都遇到了麻烦。这很容易解释。代码会生成很多竞争条件,但其中绝大多数(准确地说是 99.9999%)是无关紧要的。归根结底,重要的是 0 应该是最小结果。如果您的代码认为根 5 大于根 6,或者根 234 大于根 235,它仍然不会中断。需要有一个竞争条件,特别是迭代生成 0。其中一个迭代与另一个迭代具有竞争条件的几率非常非常高。处理最后一个项目的迭代具有竞争条件的可能性非常低。

于 2012-02-06T20:06:29.157 回答