3

C++ では、var++ や var-- などは合理的にスレッドセーフであると常に信じ込まされてきたように感じます。つまり、ある時点で値が増減することが保証されているということです。

この信念に基づいて、スレッドセーフ操作のためのノンブロッキング アルゴリズムの理解を深めました。しかし、今日、インクリメントされていない変数があるため、ショックを受けています。したがって、過去の大量の作業の妥当性に疑問を呈しています。

小さなプログラムでは、0 に初期化されたグローバル変数があります。8 つの P スレッドが起動され、それぞれが varname++ を合計 1024 回呼び出し、合計で 8*1024 インクリメントされます。しかし、すべてのスレッドの実行が終了すると、varname の値は 8*1024 より大幅に小さくなります。

ここでボートに乗り遅れたのですか?誰かが私を啓発してもらえますか?

4

3 に答える 3

5

スレッドセーフであるという信念にあなたを正確に導くものは何ですか?一般的にはそうではありません。その理由は単純です。算術演算は通常レジスターで行われるためvar++、次のようなものに変換される可能性があります。

load var to R1
inc R1
store R1 to var

ロードとストアの間で別のスレッドが変更varされた場合、明らかにその更新が失われます。var実際には、コンパイラは変数をレジスタに保持することを決定できるため、この問題はさらに悪化します(同じように、変数へのポインタを介してアクセスされていないことを証明できる限り)スレッド))。

複数のスレッドが同じ変数にアクセスすることは、(C ++ 11)標準によってデータ競合(したがって未定義の動作)として定義されます。ただし、どのスレッドも変数を変更しない場合を除きます(すべてが読み取りアクセスのみを行う場合は、問題ありません)。 。

スレッドセーフ操作の場合、ロック(std::mutexC ++ 11での使用など)またはアトミック操作のいずれかを使用する必要があります。C ++ 11を使用する場合は、スレッドセーフな変更を取得するためstd::atomic<int>のタイプとして(またはカウンターのタイプを問わず)使用できます。var(算術)std::atomic<T>(インクリメントおよびデクリメント演算子のような)操作は、標準によってスレッドセーフであることが保証されています。

于 2013-03-23T22:26:03.630 に答える
4

C++ のプレインクリメントおよびポストインクリメント演算子は、スレッドセーフではありません。

ここで同様の質問: i++ はスレッドセーフではないと聞きましたが、++i はスレッドセーフですか?

于 2013-03-23T22:23:59.983 に答える
2

はい、何かを見逃しています。つまり、読み取りと書き込みがアトミックではないということです。したがって、多数のスレッドが値を読み取り、インクリメントしてから書き戻すことができます。これらの操作がすべて「並行して」行われている場合、値は 1 だけインクリメントされます。

C++ 11 (またはブースト) の std::atomic タイプ ラッパーを使用してこれを修正できます

于 2013-03-23T22:24:18.877 に答える