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

それは幸運によって機能します。それを実行すると、doubleへの非アトミックな読み取りと書き込みが「引き裂かれた」値にならないことが幸運な場合があります。時々、非核実験とセットがそのレースが起こったときに正しい値を設定しているだけで幸運なことがあります。このプログラムが特定の結果を生み出すという保証はありません。

于 2012-02-06T19:49:53.423 に答える
5

あなたのコードは安全ではありません。それは偶然によってのみ機能します。

2つのスレッドがif同時に実行される場合、最小値の1つが上書きされます。

  • 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特定の問題では、とSqrt操作を入れ替える方が高速です。これSqrtは、単調に増加しているため可能です。

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

コードは実際には機能しません。ループで100,000回実行しましたが、8コアコンピューターで1回失敗し、次の出力が生成されました。

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%)は無関係です。1日の終わりに重要なのは、0が最小の結果であるという事実です。ルート5がルート6より大きい、またはルート234がルート235より大きいとコードが判断した場合でも、コードは壊れません。特に反復が0を生成する競合状態が存在する必要があります。反復の1つが別の反復との競合状態になる確率は、非常に高くなります。最後のアイテムを処理する反復が競合状態になる可能性は非常に低いです。

于 2012-02-06T20:06:29.157 に答える