3

このコードはスレッドセーフではないと言われました。

#include <iostream>
#include <thread>

static int sum[5];

static int get_sum()
{
    int x=0;
    for (int j=0;j<5;++j)
        x += sum[j];
    return x;
}

static void f1(int x){
    sum[x] = 1;
    std::cout << "f" <<x << ": " << sum[x] << " : " << get_sum() << std::endl;
}

int main()  {

    for (int j=0;j<5;++j)
        sum[j] = 0;

    std::thread t0(f1, 0);
    std::thread t1(f1, 1);
    std::thread t2(f1, 2);
    std::thread t3(f1, 3);
    std::thread t4(f1, 4);

    while (get_sum() != 5)  ;

    t0.join();
    t1.join();
    t2.join();
    t3.join();
    t4.join();

    std::cout << "final: " << get_sum() << std::endl;
}

プログラムが完了しない理由を誰か説明してもらえますか? get_sum の実行中の値が非決定論的であり、 cout からの出力がランダムにインターリーブされることはわかっていますが、それはプログラムの完了には関係ありません。

4

4 に答える 4

12

別のスレッドで変更されている可能性がある間、あるスレッドでは値にアクセスできない場合があります。それがルールです。

特定のプラットフォームで発生する可能性のある特定の問題について推測できます。しかし、一般的には役に立たない。なぜなら、それがうまくいかないかもしれないと考えることができるすべての可能な方法を修正できれば、コードは大丈夫だと人々が考えるようになるからだ。しかし、そうではありません。当時は考えられなかった方法で、物事がうまくいかない可能性があります。このように考え始めないでください。このタイプの推論によって引き起こされる失敗のリストは長く、これ以上取得する必要はありません。

失敗する可能性のある1つの方法は次のとおりです。コンパイラがループget_sumの前にすべての値をインライン化してレジスタにコピーしwhile、ループを永久に繰り返す可能性があります。これは法的な最適化です。コンパイラは、コードがループ中にいつでもこれらの値を読み取ることができることを確認できるため、while他のスレッドがそのループ中に値を変更することは許可されません。

さて、この質問と回答を読むと、多くの人がこのようにコードがうまくいかない可能性があるとは考えられなかったことがわかります。しかし、それは大丈夫です、彼らはそうする必要はありません。これらのルールが存在するのは、コードがうまくいかない可能性のあるすべての方法を考えるのが困難であり、おそらく不可能でさえあるためです。したがって、コードの正確さを、この超人間的な能力を持っていることに依存させないでください。

于 2012-12-05T00:58:28.210 に答える
1

プログラムは、すべての配列要素へのアクセスでデータ競合が発生し、同期なしで同時にアクセスされるため、動作が定義されていないため、形式が正しくありませf1getsum

于 2012-12-05T01:03:06.860 に答える
0

配列の合計に問題があります

于 2012-12-05T20:59:27.690 に答える
0

これは安全ではありません。複数のスレッドがグローバル合計にアクセスしているためです。get_sum() は、他のスレッドが相対位置に書き込む前または後に、不確かな sum[x] を読み取る可能性があります。

マルチスレッドの安全性を確保するには、共有リソースをミューテックスなどのロックで保護する必要があります。

于 2012-12-05T01:02:08.923 に答える