87

2つのスレッドがあります。1つはintを更新し、もう1つはそれを読み取ります。これは、読み取りと書き込みの順序が関係ない統計値です。

私の質問は、とにかくこのマルチバイト値へのアクセスを同期する必要がありますか?または、別の言い方をすれば、書き込みの一部が完了して中断され、その後読み取りが発生する可能性があります。

たとえば、0x00010000の増分値を取得する値=0x0000FFFFについて考えてみます。

気になる値が0x0001FFFFのようになる時間はありますか?確かに、タイプが大きいほど、このようなことが起こる可能性が高くなります。

私は常にこれらのタイプのアクセスを同期してきましたが、コミュニティがどう思っているのか興味がありました。

4

16 に答える 16

63

少年、なんて質問だ。答えは次のとおりです。

はい、いいえ、うーん、まあ、場合による

それはすべて、システムのアーキテクチャに帰着します。IA32 では、正しくアラインされたアドレスはアトミック操作になります。アライメントされていない書き込みはアトミックである可能性があり、使用中のキャッシュ システムによって異なります。メモリが単一の L1 キャッシュ ライン内にある場合はアトミックであり、それ以外の場合はアトミックではありません。CPU と RAM の間のバスの幅は、アトミックな性質に影響を与える可能性があります。8086 で正しく整列された 16 ビットの書き込みはアトミックでしたが、8088 での同じ書き込みはアトミックではありませんでした。 16ビットバス。

また、C/C++ を使用している場合は、共有値を揮発性としてマークすることを忘れないでください。そうしないと、オプティマイザーは変数がスレッドの 1 つで更新されないと見なします。

于 2008-09-10T14:42:36.033 に答える
48

最初は、ネイティブマシンサイズの読み取りと書き込みはアトミックであると考えるかもしれませんが、プロセッサ/コア間のキャッシュコヒーレンシなど、対処すべき問題がいくつかあります。WindowsではInterlocked*のようなアトミック操作を使用し、Linuxでは同等の操作を使用します。C ++ 0xには、これらを優れたクロスプラットフォームインターフェイスでラップするための「アトミック」テンプレートがあります。今のところ、プラットフォーム抽象化レイヤーを使用している場合は、これらの機能を提供する可能性があります。 ACEはそうです。クラステンプレートACE_Atomic_Opを参照してください。

于 2008-09-10T15:37:49.553 に答える
11

4 バイトの値を読み書きしていて、メモリ内で DWORD アライメントされていて、I32 アーキテクチャで実行している場合、読み取りと書き込みはアトミックです。

于 2008-09-10T14:41:34.367 に答える
9

はい、アクセスを同期する必要があります。C++0x ではデータ競合になり、未定義の動作になります。POSIX スレッドでは、すでに未定義の動作です。

実際には、データ型がネイティブのワード サイズよりも大きい場合、不適切な値が得られる可能性があります。また、別のスレッドは、読み取りおよび/または書き込みを移動する最適化により、書き込まれた値を認識しない場合があります。

于 2008-09-10T14:31:07.373 に答える
3

同期する必要がありますが、特定のアーキテクチャでは効率的な方法があります。

最善の方法は、条件付きで実装をプラットフォーム固有の実装に置き換えることができるように、サブルーチン (おそらくマクロの背後にマスクされる) を使用することです。

Linux カーネルには、このコードの一部が既に含まれています。

于 2008-09-10T14:38:01.693 に答える
3

Windows では、Interlocked***Exchange***Add はアトミックであることが保証されています。

于 2008-09-16T21:46:03.877 に答える
2

2 階の誰もが言ったことを反映するように、C++0x より前の言語は、複数のスレッドからの共有メモリ アクセスについて何も保証できません。保証はコンパイラ次第です。

于 2008-09-11T16:07:06.307 に答える
0

私は多くの人、特にジェイソンに同意します。Windowsでは、InterlockedAddとその仲間を使用する可能性があります。

于 2008-09-10T15:00:19.533 に答える
0

上記のキャッシュの問題は別として...

レジスタ サイズが小さいプロセッサにコードを移植すると、アトミックではなくなります。

IMO、スレッドの問題は厄介すぎてリスクを冒すことはできません。

于 2008-09-12T01:03:40.433 に答える
0

tc、定数(6など)を使用した瞬間、命令は1マシンサイクルで完了しないと思います。x++ と比較して x+=6 の命令セットを確認してみてください

于 2010-08-19T09:22:09.967 に答える
0

絶対にいいえ!C++ の最高権威である M. Boost からの回答:
「通常の」変数に対する操作は、アトミックであることが保証されていません。

于 2013-10-22T09:13:50.597 に答える
0

++c はアトミックだと考える人もいますが、生成されるアセンブリに注目しています。たとえば、「gcc -S」の場合:

movl    cpt.1586(%rip), %eax
addl    $1, %eax
movl    %eax, cpt.1586(%rip)

int をインクリメントするために、コンパイラは最初に int をレジスタにロードし、メモリに格納します。これはアトミックではありません。

于 2012-03-28T07:39:02.160 に答える
0

いいえ、そうではありません (少なくとも、そうであるとは想定できません)。そうは言っても、これをアトミックに行うにはいくつかのトリックがありますが、通常は移植性がありません ( Compare-and-swap を参照)。

于 2008-09-10T14:30:12.913 に答える
0

この例を見てみましょう

int x;
x++;
x=x+5;

最初のステートメントは、1 つの CPU サイクルを使用する 1 つの INC アセンブリ ディレクティブに変換されるため、アトミックであると見なされます。ただし、2 番目の割り当てには複数の操作が必要なため、明らかにアトミック操作ではありません。

別の例、

x=5;

ここでも、コードを逆アセンブルして、ここで正確に何が起こっているかを確認する必要があります。

于 2010-07-31T16:39:33.913 に答える
-1

移植可能な唯一の方法は、コンパイラの signal.h ヘッダーで定義された sig_atomic_t 型を使用することです。ほとんどの C および C++ 実装では、これは int です。次に、変数を「volatile sig_atomic_t」として宣言します。

于 2010-05-09T01:47:06.663 に答える