std:: memory_order
|
ヘッダーで定義
<atomic>
|
||
|
enum
memory_order
{
|
(C++11以降)
(C++20まで) |
|
|
enum
class
memory_order
:
/* 未指定 */
{
|
(C++20以降) | |
std::memory_order
は、アトミック操作を中心としたメモリアクセス(通常の非アトミックメモリアクセスを含む)の順序付け方法を指定します。マルチコアシステムにおいて制約がない場合、複数のスレッドが同時に複数の変数を読み書きすると、あるスレッドが値の変更を別のスレッドが書き込んだ順序とは異なる順序で観測することがあります。実際、変更の見かけ上の順序は複数のリーダースレッド間でも異なる可能性があります。メモリモデルによって許可されるコンパイラ変換により、ユニプロセッサシステムでも同様の効果が発生することがあります。
C++標準ライブラリにおける全てのアトミック操作のデフォルト動作は、
逐次一貫性順序
を提供します(以下の議論を参照)。このデフォルト設定はパフォーマンスに悪影響を及ぼす可能性がありますが、ライブラリのアトミック操作には追加の
std::memory_order
引数を指定することで、原子性を超えた正確な制約をコンパイラとプロセッサに強制させることができます。
定数
|
ヘッダーで定義
<atomic>
|
|
| 名前 | 意味 |
memory_order_relaxed
|
緩和された操作:他の読み書きに対する同期や順序制約はなく、この操作のアトミック性のみが保証される(以下の 緩和された順序付け を参照)。 |
memory_order_consume
(C++26で非推奨) |
このメモリ順序を持つロード操作は、影響を受けるメモリ位置に対して コンシューム操作 を実行する:現在ロードされた値に依存する現在のスレッド内の読み書きは、このロードの前に並べ替えることができない。同じアトミック変数を解放する他のスレッド内のデータ依存変数への書き込みは、現在のスレッドで可視となる。ほとんどのプラットフォームでは、これはコンパイラの最適化にのみ影響する(以下の 解放-コンシューム順序付け を参照)。 |
memory_order_acquire
|
このメモリ順序を持つロード操作は、影響を受けるメモリ位置に対して アクワイア操作 を実行する:現在のスレッド内の読み書きは、このロードの前に並べ替えることができない。同じアトミック変数を解放する他のスレッド内のすべての書き込みは、現在のスレッドで可視となる(以下の 解放-アクワイア順序付け を参照)。 |
memory_order_release
|
このメモリ順序を持つストア操作は リリース操作 を実行する:現在のスレッド内の読み書きは、このストアの後に並べ替えることができない。現在のスレッド内のすべての書き込みは、同じアトミック変数をアクワイアする他のスレッドで可視となる(以下の 解放-アクワイア順序付け を参照)。また、アトミック変数への依存関係を持つ書き込みは、同じアトミック変数をコンシュームする他のスレッドで可視となる(以下の 解放-コンシューム順序付け を参照)。 |
memory_order_acq_rel
|
このメモリ順序を持つ読み込み-変更-書き込み操作は、 アクワイア操作 と リリース操作 の両方である。現在のスレッド内のメモリ読み書きは、ロードの前に、またストアの後に並べ替えることができない。同じアトミック変数を解放する他のスレッド内のすべての書き込みは、変更の前に可視となり、変更は同じアトミック変数をアクワイアする他のスレッドで可視となる。 |
memory_order_seq_cst
|
このメモリ順序を持つロード操作は アクワイア操作 を実行し、ストア操作は リリース操作 を実行し、読み込み-変更-書き込み操作は アクワイア操作 と リリース操作 の両方を実行する。さらに、すべてのスレッドがすべての変更を同じ順序で観察する単一の全順序が存在する(以下の 逐次一貫性順序付け を参照)。 |
形式的記述
スレッド間同期とメモリ順序付けは、異なる実行スレッド間で式の 評価 と 副作用 がどのように順序付けられるかを決定します。これらは以下の用語で定義されます:
シーケンス前に
同一スレッド内では、評価Aが評価Bに対して sequenced-before の関係を持つ場合があり、これは evaluation order で説明されています。
依存関係の伝播同一スレッド内で、評価Aが評価Bに対して 先行順序付け されている場合、以下のいずれかに該当するときは、AがBに依存関係を伝播する(つまり、BがAに依存する)可能性がある:
1)
Aの値がBのオペランドとして使用される場合、
ただし以下を除く
a)
Bが
std::kill_dependency
の呼び出しである場合、
b)
Aが組み込み演算子
&&
、
||
、
?:
、または
,
の左オペランドである場合。
2)
AがスカラーオブジェクトMに書き込み、BがMから読み取る場合。
3)
Aが別の評価Xに依存関係を伝播し、XがBに依存関係を伝播する場合。
|
(C++26まで) |
変更順序
特定のアトミック変数に対するすべての変更は、そのアトミック変数に固有の全順序で発生します。
以下の4つの要件は、すべてのアトミック操作に対して保証されています:
解放シーケンス
アトミックオブジェクト M に対して release operation A が実行された後、M の modification order の中で最も長い連続部分列であり、以下から構成されるもの:
|
1)
Aを実行したのと同じスレッドによって実行される書き込み。
|
(until C++20) |
Aによって先頭とされるリリースシーケンス として知られています。
同期対象
スレッドAにおけるアトミックストアが release操作 であり、スレッドBにおける同じ変数からのアトミックロードが acquire操作 であり、かつスレッドBのロードがスレッドAのストアによって書き込まれた値を読み取る場合、スレッドAのストアはスレッドBのロードと synchronizes-with の関係が成立します。
また、一部のライブラリ呼び出しは、他のスレッド上の別のライブラリ呼び出しと synchronize-with 関係を持つように定義されている場合があります。
依存関係順序付け先行スレッド間において、以下のいずれかが真である場合、評価Aは評価Bに対して 依存関係順序付け先行 する:
1)
Aが何らかのアトミック変数Mに対して
解放操作
を実行し、かつ異なるスレッドにおいてBが同じアトミック変数Mに対して
消費操作
を実行し、かつBがAによって書き込まれた値を読み取る場合
(解放シーケンスの先頭による任意の部分)
(C++20まで)
2)
AがXに対して依存関係順序付け先行し、かつXがBに対して依存関係を伝播する場合。
|
(C++26まで) |
スレッド間 happens-before
スレッド間では、以下のいずれかが真である場合、評価Aは inter-thread happens before 評価Bとなります:
Happens-beforeスレッドに関係なく、以下のいずれかが真である場合、評価Aは評価Bに対して happens-before する:
1)
AがBに対して
sequenced-before
である。
2)
AがBに対して
inter-thread happens before
する。
実装は、 happens-before 関係が非循環的であることを保証する必要がある(必要に応じて追加の同期を導入することで、これはconsume操作が関与する場合にのみ必要となる可能性がある。詳細は Batty et al を参照)。 一方の評価がメモリ位置を変更し、もう一方の評価が同じメモリ位置を読み取りまたは変更し、かつ少なくとも一方の評価がアトミック操作でない場合、これら2つの評価の間に happens-before 関係が存在しない限り、プログラムの動作は未定義となる(プログラムは データ競合 を持つ)。
|
(C++26まで) | ||
Happens-beforeスレッドに関係なく、以下のいずれかが真である場合、評価Aは評価Bに対して happens-before する:
1)
AがBに対して
sequenced-before
である。
2)
AがBに対して
synchronizes-with
する。
3)
AがXに対して
happens-before
し、かつXがBに対して
happens-before
する。
|
(C++26以降) |
強く発生する順序 (Strongly happens-before)
スレッドに関係なく、以下のいずれかが真である場合、評価Aは評価Bに対して strongly happens-before します:
|
1)
AはBに対して
sequenced-before
である。
2)
AはBに対して
synchronizes-with
する。
3)
AはXに対して
strongly happens-before
し、XはBに対して
strongly happens-before
する。
|
(C++20まで) | ||
|
1)
AはBに対して
sequenced-before
である。
2)
AはBに対して
synchronizes with
し、かつAとBの両方が逐次一貫性アトミック操作である。
3)
AはXに対して
sequenced-before
であり、XはYに対して
simply
(C++26まで)
happens-before
し、YはBに対して
sequenced-before
である。
4)
AはXに対して
strongly happens-before
し、XはBに対して
strongly happens-before
する。
注: 非公式には、AがBに対して strongly happens-before する場合、Aはすべての文脈でBよりも前に評価されているように見える。
|
(C++20以降) |
可視副作用
スカラーMに対する副作用A(書き込み)は、以下の両方が真である場合、Mに対する値計算B(読み取り)に関して 可視 である:
副作用Aが値計算Bに対して可視である場合、Mに対する副作用の最長連続部分集合で、 変更順序 においてBがそれに 先行発生 しないものは、 可視副作用列 として知られる(Bによって決定されるMの値は、これらの副作用のいずれかによって格納された値となる)。
注意: スレッド間同期は、データ競合の防止(happens-before関係の確立による)と、どの副作用がどの条件下で可視化されるかを定義することに帰着します。
Consume操作
memory_order_consume
またはそれより強いメモリ順序でのアトミックロードはコンシューム操作です。
std::atomic_thread_fence
はコンシューム操作よりも強い同期要件を課すことに注意してください。
Acquire操作
memory_order_acquire
またはそれ以上のメモリ順序を持つアトミックロードはacquire操作です。
Mutex
に対する
lock()
操作もacquire操作です。
std::atomic_thread_fence
はacquire操作よりも強い同期要件を課すことに注意してください。
リリース操作
memory_order_release
またはそれ以上のメモリ順序を持つアトミックストアはリリース操作です。
Mutex
の
unlock()
操作もリリース操作です。
std::atomic_thread_fence
はリリース操作よりも強い同期要件を課すことに注意してください。
説明
緩和された順序付け
memory_order_relaxed でタグ付けされたアトミック操作は、同期操作ではありません。これらは並行メモリアクセス間での順序を強制しません。アトミック性と変更順序の一貫性のみを保証します。
例えば、 x と y が初期値ゼロの場合、
// スレッド1: r1 = y.load(std::memory_order_relaxed); // A x.store(r1, std::memory_order_relaxed); // B // スレッド2: r2 = x.load(std::memory_order_relaxed); // C y.store(42, std::memory_order_relaxed); // D
r1 == r2 == 42 が生成されることが許容されます。なぜなら、スレッド1内ではAがBより sequenced-before であり、スレッド2内ではCがDより sequenced before であるものの、 y の変更順序においてDがAより前に現れることや、 x の変更順序においてBがCより前に現れることを妨げるものは何もないからです。スレッド1のロードAに対して y へのDの副作用が見える可能性があり、同時にスレッド2のロードCに対して x へのBの副作用が見える可能性があります。特に、コンパイラのリオーダリングまたは実行時において、スレッド2でDがCより先に完了する場合にこれが発生し得ます。
|
緩和されたメモリモデルであっても、out-of-thin-air値が自身の計算に循環依存することは許可されません。例えば、 x と y が初期値0の場合、 // Thread 1: r1 = y.load(std::memory_order_relaxed); if (r1 == 42) x.store(r1, std::memory_order_relaxed); // Thread 2: r2 = x.load(std::memory_order_relaxed); if (r2 == 42) y.store(42, std::memory_order_relaxed); が r1 == r2 == 42 を生成することは許可されません。なぜなら、 42 の y へのストアは、 x へのストアが 42 をストアする場合にのみ可能であり、これは y へのストアが 42 をストアすることに循環依存しているからです。C++14までは、仕様上これは技術的に許可されていましたが、実装者には推奨されていなかったことに注意してください。 |
(C++14以降) |
緩和されたメモリ順序の典型的な使用例は、カウンタのインクリメントです。例えば
std::shared_ptr
の参照カウンタなどが該当します。これはアトミック性のみを要求し、順序付けや同期は必要としないためです(ただし、
std::shared_ptr
カウンタのデクリメントは、デストラクタとの間でacquire-release同期を必要とすることに注意してください)。
#include <atomic> #include <iostream> #include <thread> #include <vector> std::atomic<int> cnt = {0}; void f() { for (int n = 0; n < 1000; ++n) cnt.fetch_add(1, std::memory_order_relaxed); } int main() { std::vector<std::thread> v; for (int n = 0; n < 10; ++n) v.emplace_back(f); for (auto& t : v) t.join(); std::cout << "Final counter value is " << cnt << '\n'; }
出力:
Final counter value is 10000
Release-Acquire順序付け
スレッドAにおけるアトミックストアが memory_order_release でタグ付けされ、スレッドBにおける同じ変数からのアトミックロードが memory_order_acquire でタグ付けされ、かつスレッドBのロードがスレッドAのストアによって書き込まれた値を読み取る場合、スレッドAのストアはスレッドBのロードと synchronizes-with 関係を確立します。
スレッドAの観点からアトミックストアの前に発生したすべてのメモリ書き込み(非アトミックおよび緩和アトミックを含む)は、 happened-before 関係により、スレッドBにおいて visible side-effects となります。つまり、アトミックロードが完了すると、スレッドBはスレッドAがメモリに書き込んだすべての内容を確実に認識します。この保証は、Bが実際にAが格納した値、またはリリースシーケンスでそれ以降の値を返す場合にのみ有効です。
同期は、同じアトミック変数を releasing するスレッドと acquiring するスレッドの間でのみ確立されます。他のスレッドは、同期されたスレッドの一方または両方とは異なるメモリアクセスの順序を観測する可能性があります。
強く順序付けられたシステム(x86、SPARC TSO、IBMメインフレームなど)では、リリース-取得順序付けはほとんどの操作で自動的に行われます。この同期モードでは追加のCPU命令は発行されず、特定のコンパイラ最適化のみが影響を受けます(例えば、コンパイラは非アトミックストアをアトミックストア-リリースを超えて移動したり、非アトミックロードをアトミックロード-取得より早く実行したりすることが禁止されます)。弱く順序付けられたシステム(ARM、Itanium、PowerPC)では、特別なCPUロードまたはメモリフェンス命令が使用されます。
相互排他ロック、例えば std::mutex や atomic spinlock は、release-acquire同期の一例です:スレッドAがロックを解放し、スレッドBがそれを獲得するとき、クリティカルセクション内(解放前)でスレッドAのコンテキストで発生したすべての処理は、同じクリティカルセクションを実行しているスレッドB(獲得後)から可視である必要があります。
#include <atomic> #include <cassert> #include <string> #include <thread> std::atomic<std::string*> ptr; int data; void producer() { std::string* p = new std::string("Hello"); data = 42; ptr.store(p, std::memory_order_release); } void consumer() { std::string* p2; while (!(p2 = ptr.load(std::memory_order_acquire))) ; assert(*p2 == "Hello"); // never fires assert(data == 42); // never fires } int main() { std::thread t1(producer); std::thread t2(consumer); t1.join(); t2.join(); }
以下の例は、リリースシーケンスを使用して3つのスレッド間で推移的なrelease-acquire順序付けを示しています。
#include <atomic> #include <cassert> #include <thread> #include <vector> std::vector<int> data; std::atomic<int> flag = {0}; void thread_1() { data.push_back(42); flag.store(1, std::memory_order_release); } void thread_2() { int expected = 1; // memory_order_relaxed is okay because this is an RMW, // and RMWs (with any ordering) following a release form a release sequence while (!flag.compare_exchange_strong(expected, 2, std::memory_order_relaxed)) { expected = 1; } } void thread_3() { while (flag.load(std::memory_order_acquire) < 2) ; // if we read the value 2 from the atomic flag, we see 42 in the vector assert(data.at(0) == 42); // will never fire } int main() { std::thread a(thread_1); std::thread b(thread_2); std::thread c(thread_3); a.join(); b.join(); c.join(); }
Release-Consume順序付け
|
スレッドAにおけるアトミックストアが memory_order_release でタグ付けされ、スレッドBにおける同じ変数からのアトミックロードが memory_order_consume でタグ付けされ、かつスレッドBのロードがスレッドAのストアによって書き込まれた値を読み取る場合、スレッドAのストアはスレッドBのロードに対して 依存順序付けされる 。 スレッドAの観点からアトミックストアの前に 発生した すべてのメモリ書き込み(非アトミックおよび緩和アトミック)は、ロード操作が 依存を伝播する スレッドB内の操作において 可視的な副作用 となる。つまり、アトミックロードが完了すると、ロードから得られた値を使用するスレッドB内の演算子および関数は、スレッドAがメモリに書き込んだ内容を確実に認識する。 同期は、同じアトミック変数を 解放する スレッドと 消費する スレッドの間でのみ確立される。他のスレッドは、同期されたスレッドの一方または両方とは異なるメモリアクセスの順序を見ることがある。 DEC Alphaを除くすべての主流CPUでは、依存順序付けは自動的に行われる。この同期モードでは追加のCPU命令は発行されず、特定のコンパイラ最適化のみが影響を受ける(例えば、コンパイラは依存チェーンに関与するオブジェクトに対して投機的ロードを実行することが禁止される)。
この順序付けの典型的な使用例には、頻繁に書き込まれない並行データ構造(ルーティングテーブル、設定、セキュリティポリシー、ファイアウォールルールなど)への読み取りアクセスや、ポインタを介した公開を行うパブリッシャー-サブスクライバー状況が含まれる。つまり、プロデューサーがコンシューマーが情報にアクセスできるポインタを公開する場合、プロデューサーがメモリに書き込んだ他のすべてのものをコンシューマーに見えるようにする必要はない(弱い順序付けのアーキテクチャでは高コストな操作となり得る)。このようなシナリオの例は
詳細な依存チェーン制御については、
std::kill_dependency
および
現在(2015年2月時点)、依存チェーンを追跡する既知のプロダクションコンパイラは存在しないことに注意:consume操作はacquire操作に昇格される。 |
(C++26まで) |
|
release-consume順序付けの仕様は改訂中であり、
|
(C++17以降)
(C++26まで) |
|
release-consume順序付けはrelease-acquire順序付けと同じ効果を持ち、非推奨となりました。 |
(C++26以降) |
この例は、ポインタを介した公開における依存順序同期を示しています:整数データは文字列へのポインタとデータ依存関係を持たないため、コンシューマ側でのその値は未定義となります。
#include <atomic> #include <cassert> #include <string> #include <thread> std::atomic<std::string*> ptr; int data; void producer() { std::string* p = new std::string("Hello"); data = 42; ptr.store(p, std::memory_order_release); } void consumer() { std::string* p2; while (!(p2 = ptr.load(std::memory_order_consume))) ; assert(*p2 == "Hello"); // never fires: *p2 carries dependency from ptr assert(data == 42); // may or may not fire: data does not carry dependency from ptr } int main() { std::thread t1(producer); std::thread t2(consumer); t1.join(); t2.join(); }
逐次一貫性順序付け
memory_order_seq_cst でタグ付けされたアトミック操作は、リリース/アクquire順序と同じ方法でメモリを順序付けるだけでなく(1つのスレッドでストアの前に発生したすべてのことが、ロードを行ったスレッドで可視的な副作用となる)、このようにタグ付けされたすべてのアトミック操作の単一の全順序変更順序を確立します。
|
形式的には、
各
Bに対して
sequenced-before
関係にある
Mに対するアトミック操作のペアAとB(Aは書き込み、BはMの値を読み込み)において、2つの
Mに対するアトミック変更のペアAとBにおいて、以下の場合にBはMの変更順序においてAの後に発生する:
これは以下のことを意味する:
1)
memory_order_seq_cst
がタグ付けされていないアトミック操作が関与すると、逐次一貫性は失われる、
2)
逐次一貫性フェンスは、一般的な場合のアトミック操作ではなく、フェンス自体の全順序のみを確立する(
sequenced-before
は
happens-before
とは異なり、スレッド間の関係ではない)。
|
(C++20まで) |
|
形式的には、
あるアトミックオブジェクトMに対するアトミック操作Aが、Mに対する別のアトミック操作Bに対して coherence-ordered-before となるのは、以下のいずれかが真である場合です:
1)
Aが変更操作であり、BがAによって格納された値を読み取る場合、
2)
AがMの
modification order
においてBより前にある場合、
3)
Aがアトミック変更Xによって格納された値を読み取り、Xが
modification order
においてBより前にあり、かつAとBが同じアトミックread-modify-write操作でない場合、
4)
AがXに対して
coherence-ordered-before
であり、XがBに対して
coherence-ordered-before
である場合。
すべての
1)
AとBが
memory_order_seq_cst
操作であり、AがBに対して
強く happens-before
する場合、SにおいてAはBより前にある、
2)
オブジェクトMに対するアトミック操作AとBのペアについて、AがBに対して
coherence-ordered-before
である場合:
a)
AとBがともに
memory_order_seq_cst
操作である場合、SにおいてAはBより前にある、
b)
Aが
memory_order_seq_cst
操作であり、Bが
memory_order_seq_cst
フェンスYに対して
happens-before
する場合、SにおいてAはYより前にある、
c)
memory_order_seq_cst
フェンスXがAに対して
happens-before
し、Bが
memory_order_seq_cst
操作である場合、SにおいてXはBより前にある、
d)
memory_order_seq_cst
フェンスXがAに対して
happens-before
し、Bが
memory_order_seq_cst
フェンスYに対して
happens-before
する場合、SにおいてXはYより前にある。
この形式的定義により以下が保証されます:
1)
単一の全順序は、任意のアトミックオブジェクトの
modification order
と整合性がある、
2)
memory_order_seq_cst
ロードは、最後の
memory_order_seq_cst
変更から、または先行する
memory_order_seq_cst
変更に対して
happen-before
しない非
memory_order_seq_cst
変更から値を取得する。
単一の全順序は
happens-before
と整合性がない可能性があります。これにより、一部のCPUにおける
例えば、
// Thread 1: x.store(1, std::memory_order_seq_cst); // A y.store(1, std::memory_order_release); // B // Thread 2: r1 = y.fetch_add(1, std::memory_order_seq_cst); // C r2 = y.load(std::memory_order_relaxed); // D // Thread 3: y.store(3, std::memory_order_seq_cst); // E r3 = x.load(std::memory_order_seq_cst); // F
は
r1
==
1
&&
r2
==
3
&&
r3
==
0
という結果を生成することが許可されます。ここでAはCに対して
happens-before
しますが、Cは
注意点:
1)
memory_order_seq_cst
でタグ付けされていないアトミック操作が含まれると、プログラムに対する逐次一貫性の保証は失われます、
2)
多くの場合、
memory_order_seq_cst
アトミック操作は、同じスレッドによって実行される他のアトミック操作に対して並べ替え可能です。
|
(C++20以降) |
シーケンシャルな順序付けは、すべてのコンシューマがすべてのプロデューサのアクションを同じ順序で観測する必要がある、マルチプロデューサ-マルチコンシューマの状況で必要となる場合があります。
トータル・シーケンシャル・オーダリングは、すべてのマルチコアシステムにおいて完全なメモリフェンスCPU命令を必要とします。これは、影響を受けるメモリアクセスがすべてのコアに伝搬することを強制するため、パフォーマンスのボトルネックとなる可能性があります。
この例は、逐次一貫性の順序付けが必要な状況を示しています。他の順序付けでは、スレッド
c
と
d
がアトミック変数
x
と
y
への変更を逆の順序で観測する可能性があるため、アサートがトリガーされる可能性があります。
#include <atomic> #include <cassert> #include <thread> std::atomic<bool> x = {false}; std::atomic<bool> y = {false}; std::atomic<int> z = {0}; void write_x() { x.store(true, std::memory_order_seq_cst); } void write_y() { y.store(true, std::memory_order_seq_cst); } void read_x_then_y() { while (!x.load(std::memory_order_seq_cst)) ; if (y.load(std::memory_order_seq_cst)) ++z; } void read_y_then_x() { while (!y.load(std::memory_order_seq_cst)) ; if (x.load(std::memory_order_seq_cst)) ++z; } int main() { std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); a.join(); b.join(); c.join(); d.join(); assert(z.load() != 0); // will never happen }
volatile との関係
実行スレッド内では、 volatile glvalues を通じたアクセス(読み書き)は、同じスレッド内で sequenced-before または sequenced-after の関係にある観測可能な副作用(他のvolatileアクセスを含む)を越えて並べ替えることはできません。しかし、この順序は他のスレッドから観測されることが保証されていません。なぜならvolatileアクセスはスレッド間同期を確立しないからです。
さらに、volatileアクセスはアトミックではありません(同時読み書きは data race となります)また、メモリ順序を保証しません(非volatileメモリアクセスはvolatileアクセスの周りで自由に並べ替えられる可能性があります)。
注目すべき例外はVisual Studioであり、デフォルト設定では、すべてのvolatile書き込みはリリースセマンティクスを持ち、すべてのvolatile読み取りはアクワイアセマンティクスを持ちます(
Microsoft Docs
)。したがって、volatileはスレッド間同期に使用される可能性があります。標準的な
volatile
セマンティクスはマルチスレッドプログラミングには適用できませんが、同じスレッドで実行される
std::signal
ハンドラとの通信などには十分です(
sig_atomic_t
変数に適用される場合)。コンパイラオプション
/volatile:iso
を使用して標準に準拠した動作を復元することができ、これはターゲットプラットフォームがARMの場合のデフォルト設定です。
関連項目
|
C documentation
for
memory order
|
|
C documentation
for
memory order
|
外部リンク
| 1. | MOESIプロトコル |
| 2. | x86-TSO: x86マルチプロセッサのための厳密かつ実用的なプログラマモデル P. Sewell 他, 2010 |
| 3. | ARMおよびPOWER緩和メモリモデルのチュートリアル入門 P. Sewell 他, 2012 |
| 4. | MESIF: ポイントツーポイント相互接続のための2ホップキャッシュ一貫性プロトコル J.R. Goodman, H.H.J. Hum, 2009 |
| 5. | メモリモデル Russ Cox, 2021 |
|
このセクションは不完全です
理由: QPI、MOESI、そしておそらくDragonに関する適切な参考文献を見つけましょう。 |