2017-10-12 9 views
0

変数valAとvalBの同期をThreadMethodOneのロード時に緩和する最も正しい方法は何ですか(valAとvalBの誤ったキャッシュライン共有がないと仮定します)? valB.loadのmemory_order_acquireはvalB.loadの後にvalAを移動しないように保護していないため、コンパイラはvalB.loadの後にvalA.loadを移動できるため、memory_order_relaxedを使用するようにThreadMethodOneを変更すべきではないようですその変更が行われます。また、valB.loadにmemory_order_relaxedを使用することはできないようです。なぜなら、ThreadMethodTwoのfetch_addと同期しなくなるからです。アイテムを交換してvalAの負荷を緩和する方が良いでしょうか?C++は2つの異なる変数でmemory_order_relaxedを使用しています

これが正しい変更ですか?コンパイラのエクスプローラ上の結果を見ると

nTotal += valB.load(std::memory_order_acquire); 
nTotal += valA.load(std::memory_order_relaxed); 

は、私は命令の順序を入れ替えていない場合でも、ValaのかvalBのいずれかのためにmemory_order_relaxed使用している場合ThreadMethodOneに同じコード生成を示しているようです。私はまた、memory_order_releaseと同じになるように、ThreadMethodTwoのmemory_order_relaxedがまだコンパイルされていることも確認します。 memory_order_relaxedを次の行に変更すると、ロックされていない 'valA.store(valA.load(std :: memory_order_relaxed)+ 1、std :: memory_order_relaxed);'しかし、これが良いかどうかは分かりません。

全プログラム:

#include <stdio.h> 
#include <stdlib.h> 
#include <thread> 
#include <atomic> 
#include <unistd.h> 

bool bDone { false }; 
std::atomic_int valA {0}; 
std::atomic_int valB {0}; 

void ThreadMethodOne() 
{ 
    while (!bDone) 
    { 
     int nTotal {0}; 
     nTotal += valA.load(std::memory_order_acquire); 
     nTotal += valB.load(std::memory_order_acquire); 
     printf("Thread total %d\n", nTotal); 
    } 
} 

void ThreadMethodTwo() 
{ 
    while (!bDone) 
    { 
     valA.fetch_add(1, std::memory_order_relaxed); 
     valB.fetch_add(1, std::memory_order_release); 
    } 
} 

int main() 
{ 
    std::thread tOne(ThreadMethodOne); 
    std::thread tTwo(ThreadMethodTwo); 

    usleep(100000); 
    bDone = true; 

    tOne.join(); 
    tTwo.join(); 

    int nTotal = valA.load(std::memory_order_acquire); 
    nTotal += valB.load(std::memory_order_acquire); 
    printf("Completed total %d\n", nTotal); 
} 

それはあなたのコードをクリーンアップした後、コメント

#include <stdio.h> 
#include <stdlib.h> 
#include <thread> 
#include <atomic> 
#include <unistd.h> 

std::atomic_bool bDone { false }; 
std::atomic_int valA {0}; 
std::atomic_int valB {0}; 

void ThreadMethodOne() 
{ 
    while (!bDone) 
    { 
     int nTotalA = valA.load(std::memory_order_acquire); 
     int nTotalB = valB.load(std::memory_order_relaxed); 
     printf("Thread total A: %d B: %d\n", nTotalA, nTotalB); 
    } 
} 

void ThreadMethodTwo() 
{ 
    while (!bDone) 
    { 
     valB.fetch_add(1, std::memory_order_relaxed); 
     valA.fetch_add(1, std::memory_order_release); 
    } 
} 

int main() 
{ 
    std::thread tOne(ThreadMethodOne); 
    std::thread tTwo(ThreadMethodTwo); 

    usleep(100000); 
    bDone = true; 

    tOne.join(); 
    tTwo.join(); 

    int nTotalA = valA.load(std::memory_order_acquire); 
    int nTotalB = valB.load(std::memory_order_relaxed); 
    printf("Completed total A: %d B: %d\n", nTotalA, nTotalB); 
} 
+1

注文をやり直す必要があるのはなぜですか? – GManNickG

+1

これはトピックをよりよく理解することの問題です。 –

+0

https://www.youtube.com/watch?v=c1gO9aB9nbs 件名に関する決定的な話。 –

答えて

0

では約書かれたものだったので、元のものを残して、より良いサンプル、私のコメントを参照してください、私たちが得ます

#include <atomic> 
#include <iostream> 

std::atomic_int valA {0}; 
std::atomic_int valB {0}; 

void ThreadMethodOne() 
{ 
    int nTotalA = valA.load(std::memory_order_acquire); 
    int nTotalB = valB.load(std::memory_order_relaxed); 
    std::cout << "Thread total A: " << nTotalA << " B: " << nTotalB << '\n'; 
} 

void ThreadMethodTwo() 
{ 
    valB.fetch_add(1, std::memory_order_relaxed); 
    valA.fetch_add(1, std::memory_order_release); 
} 

int main() 
{ 
    std::thread tOne(ThreadMethodOne); 
    std::thread tTwo(ThreadMethodTwo); 

    tOne.join(); 
    tTwo.join(); 

    int nTotalA = valA.load(std::memory_order_acquire); 
    int nTotalB = valB.load(std::memory_order_relaxed); 
    std::cout << "Completed total A: " << nTotalA << " B: " << nTotalB << '\n'; 
} 

このプログラムの可能な結果は次のとおりです。

Thread total A: 0 B: 0 
Completed total A: 1 B: 1 

又は

Thread total A: 0 B: 1 
Completed total A: 1 B: 1 

又は

Thread total A: 1 B: 1 
Completed total A: 1 B: 1 

それは常にCompleted total A: 1 B: 1を印刷する理由は、スレッド2が接合され、したがって完成し、各変数に1を加えた、そしてロードされたことですスレッド1の中には何の影響もありません。

スレッド1が実行されてスレッド2よりも前に完了すると、明らかに0 0が出力され、スレッド2が実行されてスレッド1よりも前に完了すると、スレッド1は1を出力します1. memory_order_acquireスレッド1の負荷は何も強制しません。 0の初期値を簡単に読み取ることができます。

スレッドが多かれ少なかれ同時に実行される場合、0 1の結果も非常に簡単です。スレッド1は最初の行を実行し、次にスレッド2は両方を実行しますスレッド2がスレッド2によってvalBに書き込まれた値を読み込みます(リラックスしている必要はありませんが、その場合は0の出力しか得られませんが、少なくともそれは可能ですが私たちが十分に長く待っていれば、1を読んでください)。

唯一の興味深い問題は、なぜ出力が1 0にならないのでしょうか?

なぜなら、スレッド1がvalAの値1を読み取った場合、その値はスレッド2によって書き込まれた値でなければならないからです。ここでは、その値が読み出される書き込みは書き込み解放であり、読み出し自体は読み出し獲得である。これにより同期が行われ、リード・リリース後にスレッド1のすべてのメモリ・アクセスでライト・リリースより前に発生したスレッド2のすべての副作用が見えるようになります。言い換えれば、valA == 1を読み込むと、その後のvalBの読み込み(緩いかどうか)によってスレッド2のvalBへの書き込みが見られ、したがって常に1を参照します。

残念ながら私は言うことはできませんあなたの質問が非常に不明であるため、これについてもっと詳しく:私は、あなたが結果が期待していたかどうか、あるいはしたいと思っているか分からない。私はそれが起こるために必要なメモリについては何も言いません。

関連する問題