parallel.foreach 有效,但为什么呢?

发布于 2025-01-03 04:31:14 字数 462 浏览 4 评论 0原文

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

Can anyone explain, why this program is returning the correct value for 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);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

樱桃奶球 2025-01-10 04:31:14

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

It works by sheer luck. Sometimes when you run it you are lucky that the non-atomic reads and writes to the double are not resulting in "torn" values. Sometimes you are lucky that the non-atomic tests and sets just happen to be setting the correct value when that race happens. There is no guarantee that this program produces any particular result.

寒尘 2025-01-10 04:31:14

你的代码不安全;它的运作纯属巧合。

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

  • sqrt_min = 6
  • 线程 A:sqrt = 5
  • 线程 B:< code>sqrt = 4
  • 线程 A 进入 if
  • 线程 B 进入 if
  • 线程 B 赋值 sqrt_min = 4
  • 线程 A分配 sqrt_min = 5

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

在循环中使用 Interlocked.CompareExchange 可以确保安全。

Your code is not safe; it only works by coincidence.

If two threads run the if simultaneously, one of the minimums will be overwritten:

  • sqrt_min = 6
  • Thread A: sqrt = 5
  • Thread B: sqrt = 4
  • Thread A enters the if
  • Thread B enters the if
  • Thread B assigns sqrt_min = 4
  • Thread A assigns sqrt_min = 5

On 32-bit systems, you're also vulnerable to read/write tearing.

It would be possible to make this safe using Interlocked.CompareExchange in a loop.

吹梦到西洲 2025-01-10 04:31:14

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

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

您可以将代码重写为:

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

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

double sqrt_min = Math.Sqrt(myArr.AsParallel().Min())

For why your original code is broken check the other answers, I won't repeat that.

Multithreading is easiest when there is no write access to shared state. Luckily your code can be written that way. Parallel linq can be nice in such situations, but sometimes the the overhead is too large.

You can rewrite your code to:

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

In your specific problem it's faster to swap around the Min and the Sqrt operation, which is possible because Sqrt is monotonically increasing.

double sqrt_min = Math.Sqrt(myArr.AsParallel().Min())
你的呼吸 2025-01-10 04:31:14

您的代码实际上不起作用:我在循环中运行了 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();
    }
}

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

Your code does not really work: I ran it in a loop 100,000 times, and it failed once on my 8-core computer, producing this output:

minimum: 1

I shortened the runs to make the error appear faster.

Here are my modifications:

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

This is not a coincidence, considering the lack of synchronization around the reading and writing of a shared variable.

尐偏执 2025-01-10 04:31:14

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

As others have said, this only works based on shear luck. Both the OP and other posters have had trouble actually creating the race condition though. That is fairly easily explained. The code generates lots of race conditions, but the vast majority of them (99.9999% to be exact) are irrelevant. All that matters at the end of the day is the fact that 0 should be the min result. If your code thinks that root 5 is greater than root 6, or that root 234 is greater than root 235 it still won't break. There needs to be a race condition specifically with the iteration generating 0. The odds that one of the iterations has a race condition with another is very, very high. The odds that the iteration processing the last item has a race condition is really quite low.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文