2017-01-25 18 views
6

明らかに、逐次的一貫性のある原子操作は、有効なC++プログラムでは、有効な観察可能な動作と取得リリースのみの操作が異なります。定義はC++標準(C++ 11以降)またはhereで与えられています。リリースメモリの取得順序が順次整合性と異なる具体的な例は何ですか?

しかし、取得リリースセマンティクスが不十分でシーケンシャルな一貫性が必要なアルゴリズムやデータ構造の現実的な例を見逃すことはありません。

実用実世界のアルゴリズムまたはデータ構造の例で、シーケンシャル一貫性が必要であり、取得リリースメモリの順序では不十分ですか?

さらに、std::mutex does not guarantee sequential consistency

答えて

7

ピーターソンのアルゴリズムは、逐次整合性を必要とする例です。

mutexの前の日に、アルゴリズムを使用して、保護された領域への単一スレッドアクセスを提供しました。 アルゴリズムは2つのスレッドでのみ機能し、それぞれが保護領域にアクセスする意図を表すフラグを管理します。 両方ともフラグが(ほぼ)同じ時刻に設定されている場合、両方ともバックオフして再試行します。 実際のアルゴリズムは、公正なアクセスを管理するために 'turn'フラグを使用するのでより進んでいますが、seq/cstとacq/relの違いを示すために、これは必要ありません。

以下は、シーケンシャルコンシステンシーよりも弱いものが使用されている場合にアルゴリズムが壊れていることを実際に示すPetersonアルゴリズムの簡略化されたコンパイル版です。 興味深いことに、このプラットフォームではストアロードの並べ替えができるため、X86でもこの問題は解決されています。
ストアロードリオーダリングの問題点は、両方のスレッドがmeフラグをtrueに設定して、両方のスレッドがhimフラグからfalseを読み取っている間に(両方のスレッドに値が伝播していないため) )、保護区域に入る。これは逐次整合性では不可能です。 gcc

、私は必要ではなかったclangと、assert火を持っている-O3の最適化を使用してコンパイルする必要がありました。 両方のコンパイラは、異なるアプローチを使用して順次整合性を実装しています。

それはたとえば明確になり、中にバグを保つので、あなたがfetch_addとfetch_subにmemory_order_seq_cst使用する必要があります
#include <thread> 
#include <atomic> 
#include <assert.h> 

std::atomic<bool> flag1{false}; 
std::atomic<bool> flag2{false}; 

std::atomic<int> counter{0}; 

// Change these to memory_order_seq_cst to fix the algorithm 
static const auto store_ordering = std::memory_order_release; 
static const auto load_ordering = std::memory_order_acquire; 

void busy(int n) 
{ 
    auto &me = (n==1) ? flag1 : flag2; 
    auto &him = (n==1) ? flag2 : flag1; 

    for (;;) 
    { 
     for (;;) 
     { 
      me.store(true, store_ordering); 
      if (him.load(load_ordering) == false) 
      { 
       // got the 'lock' 
       break; 
      } 

      // retention, no wait period -> busy loop 
      me.store(false, store_ordering); 
     } 

     int tmp = counter.fetch_add(1, std::memory_order_relaxed); 
     assert(tmp == 0); 

     /* 
     * critical area 
     */ 

     tmp = counter.fetch_sub(1, std::memory_order_relaxed); 
     assert(tmp == 1); 

     me.store(false, store_ordering); 
    } 
} 


int main() 
{ 
    std::thread t1{busy, 1}; 
    std::thread t2{busy, 2}; 

    t1.join(); 
    t2.join(); 
} 
+0

。 – 2501

+0

2501 @ 'counter'はアルゴリズムの一部ではなく、1つだけということを証明するために、状態変数として使用あなたは一度に入ることができます。したがって、どのデータとも関係がないため、2つのRMW操作で 'relaxed'を使用しました。あなたはseq/cstがまだ例を有効にしているのは間違いありません。 – LWimsey

関連する問題