Acquire-Release Semantic - 典型的な誤解

リリース操作は、同じアトミック変数に対する取得操作と同期します。したがって、スレッドを簡単に同期できます。if ... .今日の投稿は if についてです .

取得と解放のセマンティックの典型的な誤解について投稿を書く動機は何ですか?確かに、私と私のリスナーや研修生の多くは、すでに罠にはまっています。しかし、最初は簡単なケースです。

待機中

この簡単なプログラムを出発点として使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// acquireReleaseWithWaiting.cpp

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

std::vector<int> mySharedWork;
std::atomic<bool> dataProduced(false);

void dataProducer(){
 mySharedWork={1,0,3};
 dataProduced.store(true, std::memory_order_release);
}

void dataConsumer(){
 while( !dataProduced.load(std::memory_order_acquire) );
 mySharedWork[1]= 2;
}

int main(){
 
 std::cout << std::endl;

 std::thread t1(dataConsumer);
 std::thread t2(dataProducer);

 t1.join();
 t2.join();
 
 for (auto v: mySharedWork){
 std::cout << v << " ";
 }
 
 std::cout << "\n\n";
 
}

17 行目のコンシューマー スレッド t1 は、13 行目のコンシューマー スレッド t2 が dataProduced を true に設定するまで待機しています。つまり、最初に生産者スレッド t2 が mySharedWork を初期化し、消費者スレッド t2 が mySharedWork[1] を 2 に設定して作業を終了することを意味します。したがって、プログラムは適切に定義されています。

グラフィックは前の出来事を示しています スレッド内の関係と synchronized-with スレッド間の関係。 同期 事前発生を確立します 関係。残りの推論は happens-before の推移性です 関係。 mySharedWork={1,0,3} 前に起こる mySharedWork[1]=2.

しかし、この推論に欠けていることが多い側面は何ですか? if.

もし、...

何が起きているか、もし 17 行目のコンシューマ スレッド t2 は、プロデューサ スレッドを待機していませんか?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// acquireReleaseWithoutWaiting.cpp

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

std::vector<int> mySharedWork;
std::atomic<bool> dataProduced(false);

void dataProducer(){
 mySharedWork={1,0,3};
 dataProduced.store(true, std::memory_order_release);
}

void dataConsumer(){
 dataProduced.load(std::memory_order_acquire);
 mySharedWork[1]= 2;
}

int main(){
 
 std::cout << std::endl;

 std::thread t1(dataConsumer);
 std::thread t2(dataProducer);

 t1.join();
 t2.join();
 
 for (auto v: mySharedWork){
 std::cout << v << " ";
 }
 
 std::cout << "\n\n";
 
}

変数 mySharedWork にデータ競合があるため、プログラムの動作は未定義です。プログラムを実行すると、未定義の動作がすぐに表示されます。これは、Linux と Windows にも当てはまります。

問題は何ですか?以下を保持します:store(true, std::memory_order_release) synchron ize-with dataProduced.load(std::memory_order_acquire)。はい、もちろんですが、それは取得操作が解放操作を待っているという意味ではありません。まさにそれがグラフィックに表示されます。図では、dataProduced.load(std::memory_order_acquire) 命令が dataProduced.store(true, std::memory_order_release) 命令の前に実行されます。したがって、同期対象はありません 関係。

解決策

この特定のケースでの synchronize-with の意味:If dataProduced.store(true, std::memory_order_release) は dataProduced.load(std::memory_order_acquire) の前に発生し、その後 dataProduced.store(true, std::memory_order_release) の前に表示されるすべての操作の効果は、dataProduced.load(std::memory_order_acquire) の後に表示されます。鍵は if. という言葉です。 まさにその if (while(!dataProduced.load(std::memory_order_acquire))を使用して最初のプログラムで保証されます。

繰り返しますが、正式です。

  • dataProduced.store(true, std::memory_order_release) の前のすべての操作happens-before dataProduced.load(std::memory_order_acquire) の後のすべての操作:dataProduced.store(true, std::memory_order_release) 前に起こる dataProduced.load(std::memory_order_acquire).

次は?

アトミック変数の操作による取得と解放のセマンティック。これは機能しますか?ええ、フェンスで。次の投稿をご覧ください。