27

これについての詳細を探してみました。mutex とアトミックに関する標準も読みましたが、それでも C++11 メモリ モデルの可視性の保証を理解できませんでした。私が理解していることから、ミューテックス BESIDE 相互排除の非常に重要な機能は、可視性を確保することです。また、一度に 1 つのスレッドだけがカウンターを増加させるだけでは十分ではありません。最後にミューテックスを使用したスレッドによって格納されたカウンターをスレッドが増加させることが重要です (議論するときに人々がこれについてこれ以上言及しない理由が本当にわかりませんミューテックス、多分私は悪い教師を持っていました:))。したがって、私が言えることから、アトミックは即時の可視性を強制しません: (boost::thread を維持し、c++11 スレッドとミューテックス ライブラリを実装した人から):

memory_order_seq_cst を使用したフェンスは、他のスレッドへの即時の可視性を強制しません (MFENCE 命令も同様です)。C++0x メモリの順序付け制約は、まさにそれです --- 順序付け制約です。memory_order_seq_cst 操作は全体の順序を形成しますが、すべてのスレッドで合意する必要があることと、他の順序制約に違反してはならないことを除いて、その順序に制限はありません。特に、スレッドが制約と一致する順序で値を参照する場合、スレッドはしばらくの間「古い」値を参照し続ける可能性があります。

そして、私はそれで大丈夫です。しかし、問題は、アトミックに関する C++11 の構造が「グローバル」であり、アトミック変数の一貫性のみを保証するものを理解するのに苦労していることです。特に、ロードとストアの前後にメモリフェンスが存在することを保証する次のメモリ順序のどれが(もしあれば)理解しています: http://www.stdthread.co.uk/doc/headers/atomic/memory_order. html

私が言えることから、 std::memory_order_seq_cst はメモリバリアを挿入しますが、他のものは特定のメモリ位置での操作の順序付けのみを強制します。

だから誰かがこれを片付けることができますか、多くの人が std::atomic を使用して恐ろしいバグを作っていると思います.espは、デフォルトを使用しない場合(std::memory_order_seq_cstメモリ順序付け)
2.私が正しければ、それはそれを意味しますこのコードでは、2 行目は冗長です。

atomicVar.store(42);
std::atomic_thread_fence(std::memory_order_seq_cst);  

3. do std::atomic_thread_fence には、非アトミック var で seq の一貫性を確保するために std::atomic_thread_fence(std::memory_order_seq_cst); を実行する必要があるという意味で、ミューテックスと同じ要件があります。ロード前と std::atomic_thread_fence(std::memory_order_seq_cst);
アフターストア?
4.は

  {
    regularSum+=atomicVar.load();
    regularVar1++;
    regularVar2++;
    }
    //...
    {
    regularVar1++;
    regularVar2++;
    atomicVar.store(74656);
  }

に相当

std::mutex mtx;
{
   std::unique_lock<std::mutex> ul(mtx);
   sum+=nowRegularVar;
   regularVar++;
   regularVar2++;
}
//..
{
   std::unique_lock<std::mutex> ul(mtx);
    regularVar1++;
    regularVar2++;
    nowRegularVar=(74656);
}

ないと思いますが、確認したいと思います。

編集: 5.発砲をアサートできますか?
2 つのスレッドのみが存在します。

atomic<int*> p=nullptr; 

最初のスレッドの書き込み

{
    nonatomic_p=(int*) malloc(16*1024*sizeof(int));
    for(int i=0;i<16*1024;++i)
    nonatomic_p[i]=42;
    p=nonatomic;
}

2 番目のスレッドの読み取り

{
    while (p==nullptr)
    {
    }
    assert(p[1234]==42);//1234-random idx in array
}
4

2 に答える 2

28

フェンスを扱いたい場合は、 then のa.load(memory_order_acquire)a.load(memory_order_relaxed)atomic_thread_fence(memory_order_acquire). 同様に、a.store(x,memory_order_release)は を呼び出すatomic_thread_fence(memory_order_release)前に を呼び出すのと同じa.store(x,memory_order_relaxed)です。memory_order_consumeは、memory_order_acquire依存データのみの特殊なケースです。memory_order_seq_cstは特殊であり、すべての操作にわたって全体的な順序を形成しmemory_order_seq_cstます。他のものと混合すると、ロードの場合は取得、ストアの場合はリリースと同じです。memory_order_acq_rel読み取り-変更-書き込み操作用であり、RMW の読み取り部分での取得と書き込み部分での解放に相当します。

アトミック操作で順序制約を使用すると、ハードウェア アーキテクチャに応じて、実際のフェンス命令が発生する場合と発生しない場合があります。個別のフェンスを使用するのではなく、アトミック操作に順序制約を設定すると、コンパイラがより適切なコードを生成する場合があります。

x86 では、ロードは常に取得され、ストアは常に解放されます。命令またはプレフィックス付き命令のmemory_order_seq_cstいずれかを使用して、より強力な順序付けが必要です (ストアの順序付けを強化するかロードにするかについては、実装の選択があります)。したがって、スタンドアロンの取得フェンスと解放フェンスはノーオペレーションですが、そうではありません (ここでもor ed 命令が必要です)。MFENCELOCKatomic_thread_fence(memory_order_seq_cst)MFENCELOCK

順序付け制約の重要な効果は、他の操作を順序付けることです。

std::atomic<bool> ready(false);
int i=0;

void thread_1()
{
    i=42;
    ready.store(true,memory_order_release);
}

void thread_2()
{
    while(!ready.load(memory_order_acquire)) std::this_thread::yield();
    assert(i==42);
}

thread_2trueから読み取るまで回転しますready。ストアへのストアreadythread_1リリースであり、ロードは取得であるため、ストアはロードと同期し、ストアへのストアはアサート内のロードi の前に発生iし、アサートは起動しません。

2) の 2 行目

atomicVar.store(42);
std::atomic_thread_fence(std::memory_order_seq_cst);  

store toはデフォルトで使用されるため、実際には冗長になる可能性があります。ただし、このスレッドに他の非アトミック操作がある場合、フェンスが影響を与える可能性があります。たとえば、後続の のリリース フェンスとして機能します。atomicVarmemory_order_seq_cstmemory_order_seq_csta.store(x,memory_order_relaxed)

3) フェンスとアトミック操作はミューテックスのようには機能しません。それらを使用してミューテックスを構築できますが、それらはそれらのようには機能しません。を使用する必要はありませんatomic_thread_fence(memory_order_seq_cst)。上記の例のように、アトミック操作がmemory_order_seq_cstである必要はなく、非アトミック変数の順序付けはなしで実現できます。

4) いいえ、これらは同等ではありません。したがって、ミューテックスロックのないスニペットはデータ競合であり、未定義の動作です。

5) いいえ、あなたのアサートは発火できません。memory_order_seq_cst の既定のメモリ順序では、アトミック ポインターからのpストアとロードは、上記の例のストアとロードのように機能し、配列要素へのストアは読み取りの前に発生することが保証されます。

于 2011-10-19T16:54:50.807 に答える
7

私が言えることから、 std::memory_order_seq_cst はメモリバリアを挿入しますが、他のものは特定のメモリ位置での操作の順序付けのみを強制します。

それは、あなたが何をしているか、どのプラットフォームで作業しているかに大きく依存します。std::memory_order_seq_cstx86 のようなプラットフォームでの強力なメモリ順序付けモデルは、IA64、PowerPC、ARM などのプラットフォームでの弱い順序付けモデルと比較して、メモリ フェンス操作の存在について異なる一連の要件を作成します。プラットフォームに応じて、適切なメモリ フェンス命令が使用されます。x86 のようなプラットフォームでは、読み取り-変更-書き込み操作を実行しない限り、完全なメモリ バリアは必要ありません。x86 メモリ モデルによると、すべての読み込みには読み込みと取得のセマンティクスがあり、すべてのストアには保存と解放のセマンティクスがあります。したがって、これらの場合、std::memory_order_seq_cstx86 のメモリ モデルでは、これらのタイプの操作がスレッド全体で一貫していることが既に保証されているため、enum は基本的に no-op を作成します。したがって、これらのタイプの部分的なメモリ バリアを実装するアセンブリ命令はありません。したがって、 x86 で明示的にstd::memory_order_releaseまたはを設定すると、同じノーオペレーション条件が真になります。std::memory_order_acquireさらに、このような状況で完全なメモリ バリアを必要とすることは、不要なパフォーマンスの障害になります。前述のように、これは read-modify-store 操作にのみ必要です。

ただし、メモリ一貫性モデルが弱い他のプラットフォームでは、そうではないため、使用std::memory_order_seq_cstすると適切なメモリ フェンス操作が採用され、ユーザーは、ロード-取得、ストア-リリース、またはフル メモリを希望するかどうかを明示的に指定する必要がなくなります。フェンス操作。これらのプラットフォームには、このようなメモリ整合性コントラクトを強制するための特定のマシン命令があり、std::memory_order_seq_cst設定は適切なケースで機能します。ユーザーがこれらの操作のいずれかを明示的に呼び出したい場合は、明示的なstd::memory_order列挙型を使用できますが、必要ではありません...コンパイラは正しい設定を解決します。

多くの人が std::atomic を使用して恐ろしいバグを作っていると思います。特に、デフォルトを使用しない場合 (std::memory_order_seq_cst メモリ順序付け)

はい、彼らが何をしているのかわからず、特定の操作で呼び出されるメモリバリアセマンティクスのタイプを理解していない場合、タイプを明示的に述べようとすると、多くの間違いが発生します。特に、プラットフォームは本質的に弱いため、メモリの順序付けの誤解を助けることはありません。

最後に、ここで発生する必要がある 2 つの異なることがあるミューテックスに関する状況 #4 に注意してください。

  1. コンパイラは、mutex とクリティカル セクション全体で操作を並べ替えることを許可してはなりません (特に最適化コンパイラの場合)。
  2. クリティカル セクションおよびミューテックス変数の読み取りの前にすべてのストアが完了し、クリティカル セクションを終了する前にすべてのストアが完了している状態を維持する、(プラットフォームに応じて) 必要なメモリ フェンスが作成されている必要があります。

デフォルトでは、アトミックストアとロードはstd::memory_order_seq_cstの場合、アトミックを使用すると、条件 1 と 2 を満たす適切なメカニズムも実装されます。そうは言っても、アトミックを使用した最初の例では、ロードはブロックの取得セマンティクスを強制し、ストアはリリース セマンティクスを強制します。ただし、これら 2 つの操作の間の「クリティカル セクション」内で特定の順序を強制することはありません。2 番目の例では、ロックを含む 2 つの異なるセクションがあり、各ロックには取得セマンティクスがあります。ある時点で、リリース セマンティクスを持つロックを解放する必要があるため、2 つのコード ブロックは同等ではありません。最初の例では、ロードとストアの間に大きな「クリティカル セクション」を作成しました (これがすべて同じスレッドで行われていると仮定します)。2 番目の例では、2 つの異なるクリティカル セクションがあります。

PS 次の PDF が特に有益であることがわかりました

于 2011-09-18T17:18:55.243 に答える