-
21-09-2019 - |
質問
アトミック命令とはどういう意味ですか?
以下はどのようにして Atomic になるのでしょうか?
テストアンドセット
int TestAndSet(int *x){
register int temp = *x;
*x = 1;
return temp;
}
ソフトウェアの観点から見ると、ノンブロッキング同期プリミティブを使用したくない場合、どのようにして命令のアトミック性を確保できるでしょうか?ハードウェアでのみ可能ですか、それともアセンブリレベルのディレクティブの最適化を使用できますか?
解決
いくつかのマシン命令は、本質的にアトミックです - 。たとえば、ネイティブプロセッサのワードサイズの適切整列値を読み書きすることはアトミックであるの多くのアーキテクチャ上のの
これは、ハードウェア割り込み、他のプロセッサとハイパースレッドが読み取りまたは格納を中断し、同じ場所に部分的値を読み書きすることができないことを意味します。
などアトミック一緒に読み書きなどのより複雑なものは、例えば、明示的なアトミックマシン命令によって達成することができますx86でのLOCK CMPXCHGます。
のロックおよび他の高レベルの構築物は、典型的には単一のプロセッサ・ワードを守るこれらの原子のプリミティブ上に構築されます。
いくつかの巧妙な同時のアルゴリズムは、例えば、ポインタのちょうど読み書きを使用して構築することができますリンクされたリストで複数のリーダーやライター、単一リーダライタ間、または努力と共有ます。
他のヒント
アトミックは「不可分」という意味のギリシャἄτομος(アトモス)から来ています。 の(警告:私はので、多分それは本当に何か他のものだが、語源を理由にほとんど英語を話す人はこのようにそれを解釈し、ギリシャ語を話すことはありません: - 。)の
コンピューティングでは、この操作は、よく、 が起こることを意味します。それが完了する前に見えるのです任意の中間状態はありません。だからあなたのCPUは、サービス、ハードウェア(IRQ)に中断された、または別のCPUが同じメモリを読み取っている場合、それが結果に影響を与えませんし、完了または起動していないのどちらかとして、これらの他の操作は、それを観察する場合。
例として...あなたはそれが以前に設定されていない場合にのみ、何かに変数を設定したいとしましょう。あなたがこれを行うために傾斜するかもしれません。
if (foo == 0)
{
foo = some_function();
}
しかし、この場合は並列で実行されますか?これは、プログラムをゼロとして、それを参照してください、foo
をフェッチすることが考えられ、一方、スレッド2は、一緒に来て、同じことをして、何かに値を設定します。戻る元のスレッドでは、コードはまだfoo
がゼロであると考え、その変数は二回割り当てられます。
このような場合のために、CPUは、比較アトミックエンティティとして条件付き割り当てを行うことができ、いくつかの命令を提供します。したがって、テスト・アンド・セット、スワップを比較アンド、および負荷結合/ストア条件。あなたはロックを実装するために(あなたのOSとあなたのCライブラリがこれを行っています。)または、あなたが何かをするプリミティブに依存している一回限りのアルゴリズムを記述することができ、これらを使用することができます。 (そこクールなものがここで行われるのですが、ほとんど単なる人間はそれが間違って得ることの恐れのためにこれを避けてください。)
アトミックは重要な概念である。
問題がよく例に示されています。あなたがファイルを作成したいが、ファイルが既に存在しない場合にのみ、二つのプログラムがあるとしましょう。 2つのプログラムのいずれかが任意の時点でファイルを作成することができます。
あなたが(それはあなたの例では何なので、私はCを使用します)を行う場合:
...
f = fopen ("SYNCFILE","r");
if (f == NULL) {
f = fopen ("SYNCFILE","w");
}
...
あなたは他のプログラムは、読み取りのために開いていると、書き込みのために開いている間にファイルを作成していないことを確認することはできません。
あなたが自分でこれを行うことができる方法はありません、あなたは、この目的のために通常のsyncronizationプリミティブを提供することを、オペレーティングシステムからの助けを必要とする、または、例えばアトミックであることが保証されている別のメカニズム(リレーショナルデータベースのロック操作)原子、またはプロセッサ「テストと設定」命令のような低レベルの機構である。
以下は、意味を理解するのに役立つかもしれない Atomicity に関する私のメモの一部です。注は最後にリストされている情報源からのものであり、より徹底的な説明が必要な場合は、私のように点状の箇条書きではなく、いくつかを読むことをお勧めします。間違いがあれば修正しますのでご指摘ください。
意味 :
- 「小さな部分に分割できない」を意味するギリシャ語に由来
- 「アトミック」操作は常に行われるか、行われていないことが観察されますが、途中で行われることはありません。
- 原子操作は完全に実行されるか、まったく実行されない必要があります。
- マルチスレッドシナリオでは、変数は、「中途半端な変異」値はありません。
例 1:アトミックオペレーション
さまざまなスレッドで使用される次の整数を考慮してください。
int X = 2; int Y = 1; int Z = 0; Z = X; //Thread 1 X = Y; //Thread 2
上の例では、2 つのスレッドが X、Y、Z を使用します。
- それぞれの読み取りと書き込みはアトミックです
- スレッドは競合します:
- スレッド 1 が勝った場合、Z = 2
- スレッド 2 が勝った場合、Z=1
- Z は間違いなくこれら 2 つの値のいずれかになります
例 2:非アトミック操作:++/-- 操作
インクリメント/デクリメント式を考えてみましょう。
i++; //increment i--; //decrement
操作は次のように変換されます。
- 読んでください
- 読み取り値を増減します。
- 新しい値を i に書き戻します
- 操作はそれぞれ 3 つのアトミック操作で構成されており、それ自体はアトミックではありません
- 別々のスレッドで i をインクリメントしようと 2 回試行すると、インターリーブが発生して、インクリメントの 1 つが失われる可能性があります。
例 3 - 非アトミック操作:4バイトを超える値
- 次の不変の struct を考えてみましょう。
struct MyLong { public readonly int low; public readonly int high; public MyLong(int low, int high) { this.low = low; this.high = high; } }
MyLong 型の特定の値を持つフィールドを作成します。
MyLong X = new MyLong(0xAAAA, 0xAAAA); MyLong Y = new MyLong(0xBBBB, 0xBBBB); MyLong Z = new MyLong(0xCCCC, 0xCCCC);
スレッド セーフなしで別のスレッドでフィールドを変更します。
X = Y; //Thread 1 Y = X; //Thread 2
.NET では、値型をコピーするときに CLR はコンストラクターを呼び出しません。一度に 1 つのアトミック操作でバイトを移動します。
- このため、2 つのスレッドの操作は 4 つのアトミック操作になりました。
- スレッドセーフが強制されていない場合、データが破損する可能性があります
次の操作の実行順序を考慮してください。
X.low = Y.low; //Thread 1 - X = 0xAAAABBBB Y.low = Z.low; //Thread 2 - Y = 0xCCCCBBBB Y.high = Z.high; //Thread 2 - Y = 0xCCCCCCCC X.high = Y.high; //Thread 1 - X = 0xCCCCBBBB <-- corrupt value for X
操作をアトミックにするために何らかのロックを追加せずに、32 ビット オペレーティング システム上の複数のスレッドで 32 ビットを超える値の読み取りと書き込みを行うと、上記のようにデータが破損する可能性があります。
プロセッサーの操作
最新のすべてのプロセッサでは、次の条件を満たす限り、自然にアライメントされたネイティブ型の読み取りと書き込みはアトミックであると想定できます。
- 1 :メモリ バスの幅は、読み取りまたは書き込みされる型と少なくとも同じです。
- 2 :CPU はこれらの型を 1 つのバス トランザクションで読み書きするため、他のスレッドがこれらの型が途中で完了した状態であることを認識できなくなります。
x86 および X64 では、8 バイトを超える読み取りと書き込みがアトミックであるという保証はありません。
- プロセッサ ベンダーは、各プロセッサのアトミック操作を定義します。 ソフトウェア開発者向けマニュアル
- シングル プロセッサ/シングル コア システムでは、標準のロック技術を使用して CPU 命令の中断を防ぐことができますが、これは非効率的になる可能性があります。
- 可能であれば、割り込みを無効にすることもより効率的な解決策です。
- マルチプロセッサ/マルチコア システムでもロックを使用することは可能ですが、単一の命令を使用するか割り込みを無効にするだけではアトミック アクセスは保証されません。
- アトミック性は、使用される命令がバス上で「LOCK」信号を確実にアサートし、システム内の他のプロセッサが同時にメモリにアクセスすることを防ぐことで実現できます。
言語の違い
C#
- C# は、最大 4 バイトを必要とする組み込み値型の操作がアトミックであることを保証します。
- 4 バイトを超える値の型 (double、long など) の操作はアトミックであることが保証されません。
- CLI は、プロセッサの自然なポインター サイズのサイズ (またはそれ以下) の値型の変数の読み取りと書き込みがアトミックであることを保証します。
- 例 - CLR の 64 ビット バージョンの 64 ビット OS 上で C# を実行すると、64 ビットの double と長整数の読み取りと書き込みがアトミックに実行されます。
- アトミック操作の作成:
- .NET は、System.Threading 名前空間の一部として Interlocked Class を提供します。
- Interlocked クラスは、インクリメント、比較、交換などのアトミック操作を提供します。
using System.Threading; int unsafeCount; int safeCount; unsafeCount++; Interlocked.Increment(ref safeCount);
C++
- C++ 標準はアトミックな動作を保証しません
- すべての C / C++ 操作は、コンパイラーまたはハードウェア ベンダーによって別途指定されていない限り、非アトミックであると想定されます (32 ビット整数の割り当てを含む)。
- アトミック操作の作成:
- C++ 11 同時実行ライブラリには、Atomic Operations Library () が含まれています。
- アトミック ライブラリは、任意の型で使用できるテンプレート クラスとしてアトミック タイプを提供します。
- アトミックタイプの操作はアトミックであるため、スレッドセーフです
構造体アトミックカウンター
{std::atomic< int> value; void increment(){ ++value; } void decrement(){ --value; } int get(){ return value.load(); }
}
ジャワ
- Java は、最大 4 バイトを必要とする組み込み値型の操作がアトミックであることを保証します。
- 揮発性の Long および Double への代入もアトミックであることが保証されます
- Java は、java.util.concurrent.atomic を通じて単一変数に対するロックフリーのスレッドセーフ プログラミングをサポートするクラスの小さなツールキットを提供します。
- これにより、コンペア アンド スワップ (CAS) (コンペア アンド セットとも呼ばれる) などの低レベルのアトミック ハードウェア プリミティブに基づいたアトミックなロックフリー操作が提供されます。
- CAS フォーム - boolean CompareAndSet(expectedValue, updateValue );
- このメソッドは、変数が現在 ExpectValue を保持している場合、変数を updateValue にアトミックに設定します。成功すると true が報告されます。
- CAS フォーム - boolean CompareAndSet(expectedValue, updateValue );
import java.util.concurrent.atomic.AtomicInteger; public class Counter { private AtomicInteger value= new AtomicInteger(); public int increment(){ return value.incrementAndGet(); } public int getValue(){ return value.get(); } }
アトミックはOSによって保証することができます。 OSはこれを達成するための基盤となるプロセッサの機能を使用します。
だからあなた自身のテスト・アンド・セット関数を作成することは不可能です。 (私は1つは、インラインのasmスニペットを使用できるかどうかわからないんだけど、直接テスト・アンド・セットのニーモニックを使用していますが()この文は唯一のOSのpriviligesで行うことができるというのだろう)。
編集: この記事の下のコメントによると、ASMディレクティブを使用して独自の「bittestandset」関数を作ることは、直接(インテルx86の)可能です。これらのトリックはまた、他のプロセッサ上で動作する場合は、明確ではありません。
私は私のポイントに立つ:あなたは、atmoic物事を行うOSの機能を使用すると、それを自分で行わない場合は、