2017-02-02 5 views
1

が測定されている更新し、いくつかのプロパティの平均を返すための2つの機能があると言うの平均計算するセマンティクスを取得:リリース

void Class::Update(int delta) 
{ 
    m_accumulatedValue += delta; 
    ++ m_count; 
} 

double Class::GetAverage() 
{ 
    return m_accumulatedValue/(double)m_count; 
} 

は今、彼らはスレッドとマルチスレッド環境で実行するように変更する必要があるとしいずれかのスレッドがそれらのいずれかを実行するように要求することができるプール - つまり、実行中のスレッドは、それらの一つ一つが毎回異なるものになります

std::atomic<int> m_accumulatedValue; 
std::atomic<int> m_count; 

// ... 

void Class::Update(int delta) 
{ 
    m_accumulatedValue.fetch_add(delta , std::memory_order_relaxed); 
    m_count.fetch_add(1 , std::memory_order_release); 
} 

double Class::GetAverage() 
{ 
    auto count = m_count.load(std::memory_order_acquire); 
    auto acc = m_accumulatedValue.load(std::memory_order_relaxed); 

    return acc/(double)count; 
} 

私は取得を理解し、メモリを解放しようとしています注文。

同じオブジェクトにUpdate()の同時呼び出しはありませんが、Update()GetAverage()の同じオブジェクトに対する同時呼び出しである可能性があります。私が読んだ何のため

GetAverage()m_countの取得負荷が同時に前m_accumulatedValueの負荷の並べ替えを禁止保証Update()によって行わm_accumulatedValueへの変更は、スレッドの呼び出しによって表示されていることGetAverage() への変更が一度見られる場合は、m_coutで行われた店舗ではUpdate()がリリース注文しています。

私はちょうど正しいと言いましたか?

GetAverage()Update()への呼び出しの並行性が保証されています)は常に正しい答えを返しますか?または、計算された平均値を、「更新された」値のうちのいくつかを他のものよりも戻す方法がありますか?

m_accumulatedValueはすべて原子である必要がありますか?

+1

'memory_order_relaxed'は、他のスレッドが新しい値を見るのを禁じていませんが、それだけでは必要ありません。だからまだ競争状態になる可能性があります。 –

+2

メモリオーダーセマンティクスとは別に、このコードは壊れています。実行可能な命令の1つは、あるスレッドが 'm_accumulatedValue'に追加し、別のスレッドが' m_accumulatedValue' **と** 'm_count'を読み込むと、最初のスレッドは' m_count'を更新します。 2番目のスレッドによって計算された平均は間違っています。 –

+0

No.いいえ、なぜ相互排除を使用しないのですか? – jotik

答えて

0

取得/解放のセマンティクスの仕組みの説明は正しいです。 これらは、スレッド間のスレッドを作成するために使用されますは、ストア/リリース前とロード/取得後のメモリ操作の間に、が定義されています。 atomic load/acquireは、ストア/リリースによって設定された値を参照します。

コードの最初の問題は、この実行時要件を満たしていないことです。 m_countの値はチェックされていないため、発注の保証は適用されません。したがって、すべての操作でmemory_order_relaxedを使用することもできます。

しかし、それだけでは問題は解決しません。 m_accumulatedValueを読むと、その値はUpdate()m_accumulatedValueのアトミックでなければならない)への別の呼び出しによって再び変更された可能性があります。 さらに、コメントセクションで指摘したように、Update()が完了して間違った値を返す前に、GetAverage()が呼び出されることがあります。

Update()GetAverage()の間で厳密な注文が必要です。これを実行する最善の方法はstd::mutexです。原子変数は、(他の場所で使用されていない場合は)通常の整数になります。