9

memory_order_relaxed の詳細を理解しようとしています。私はこのリンクを参照しています:CPPリファレンス

#include <future>
#include <atomic>

std::atomic<int*> ptr {nullptr};

void fun1(){
        ptr.store(new int{0}, std::memory_order_relaxed);
}

void fun2(){
        while(!ptr.load(std::memory_order_relaxed));
}

int main(){
        std::async(std::launch::async, fun1);
        std::async(std::launch::async, fun2);
}

質問 1: 上記のコードで、ptr を設定するスレッドが実行を終了した場合でも、fun2 が ptr の値を nullptr と見なす無限ループに陥ることは技術的に可能ですか?

もしそうなら、代わりに上記のコードを次のように変更します。

#include <future>
#include <atomic>

std::atomic<int> i {0};
std::atomic<int*> ptr {nullptr};

void fun1(){
        i.store(1, std::memory_order_relaxed);
        i.store(2, std::memory_order_relaxed);
        ptr.store(new int{0}, std::memory_order_release);

}

void fun2(){
        while(!ptr.load(std::memory_order_acquire));
        int x = i.load(std::memory_order_relaxed);
}

int main(){
        std::async(std::launch::async, fun1);
        std::async(std::launch::async, fun2);
}

関連する質問: 上記のコードで fun2 がアトミック i の値を 1 と見なすことは可能ですか、それとも値 2 と見なされることが保証されていますか?

4

1 に答える 1

15

興味深いのは、このコードには実際の同時実行性がないということです。つまりfun1fun2順次実行されます。その理由は、特定の条件下 (起動ポリシーstd::asyncでの呼び出しを含む) では、起動された関数呼び出しが戻るまで、によって返されるオブジェクトにデストラクタ ブロックがあるためです。戻りオブジェクトを無視するため、そのデストラクタはステートメントの終了前に呼び出されます。の 2 つのステートメントを逆にした場合(つまり、 launch before )、プログラムは実行されないため、無限ループに陥っていたでしょう。std::launch::asyncstd::futurestd::asyncmain()fun2fun1fun1

このstd::future破棄待ちの動作は (標準化委員会内でも) 多少物議を醸していますmain

auto tmp1 = std::async(std::launch::async, fun1);
auto tmp2 = std::async(std::launch::async, fun2);

これにより、実際のstd::future戻りオブジェクトの破棄が最後まで延期されるmainため、fun1非同期fun2で実行されます。

ptr を設定するスレッドの実行が終了した場合でも、fun2 が ptr の値を nullptr と見なす無限ループに陥ることは技術的に可能ですか?

いいえ、これは不可能std::atomicです (コメントセクションで述べたように、実際のプラットフォームでは)。非std::atomic変数の場合、コンパイラは (理論的には) 値をレジスタのみに保持することを選択できますが、変数std::atomicは格納され、キャッシュ コヒーレンシによって値が他のスレッドに伝達されます。std::memory_order_relaxedポインターを逆参照しない限り、ここで使用しても問題ありません。

上記のコードで fun2 がアトミック i の値を 1 と見なすことは可能ですか、それとも値 2 と見なされることが保証されていますか?

variable に値 2 が表示されることが保証されていますx
fun1は 2 つの異なる値を同じ変数に格納しますが、明確な依存関係があるため、これらは並べ替えられません。

ではfun1ptr.storewithはwithがリリース バリアの下に移動するのをstd::memory_order_release防ぎます。では、withは、 withが取得バリアを越えて上に移動するのを防ぎます。これにより、 in の値が 2 になることが 保証されます。i.store(2)std::memory_order_relaxedfun2ptr.loadstd::memory_order_acquirei.loadstd::memory_order_relaxedxfun2

すべてのアトミックで使用std::memory_order_relaxedすることにより、およびに関するアトミック変数へのアクセスの相対的な順序に応じて、値 0、1、または 2 で表示できることに注意してください。 xiptr.storeptr.load

于 2016-07-31T00:30:13.460 に答える