3

AnthonyWilliamsの本「C++Concurrency」のメモリモデルで例をテストしていました

#include<atomic>
#include<thread>
#include<cassert>

std::atomic_bool x,y;
std::atomic_int z;

void write_x_then_y() {
  x.store(true, std::memory_order_relaxed);
  y.store(true, std::memory_order_relaxed);
}

void read_y_then_x() {
  while(!y.load(std::memory_order_relaxed));
  if(x.load(std::memory_order_relaxed)) {
    ++z;
  }
}

int main() {
  x = false;
  y = false;
  z = 0;
  std::thread a(write_x_then_y);
  std::thread b(read_y_then_x);
  a.join();
  b.join();
  assert(z.load()!=0);
}

説明によると、差分変数(ここではxとy)の緩和された操作は自由に並べ替えることができます。しかし、私は問題を数日以上繰り返し実行しました。アサーション(assert(z.load()!= 0);)が発生する状況に遭遇したことはありません。デフォルトの最適化を使用し、g ++ -std = c ++ 11 -lpthread dataRaceAtomic.cppを使用してコードをコンパイルします。誰かが実際にそれを試し、アサーションをヒットしますか?誰かが私のテスト結果について説明してもらえますか?ところで、アトミックタイプを使わないバージョンも試してみましたが、同じ結果になりました。現在、両方のプログラムは正常に実行されています。ありがとう。

4

4 に答える 4

8

これは、実行しているプロセッサのタイプによって異なります。

x86には、他のプロセッサほどリラックスしたメモリモデルはありません。特に、他の店舗に関して店舗が再注文されることはありません。

http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/には、x86のメモリモデルに関する詳細情報があります。

于 2012-08-31T21:52:36.460 に答える
3

アトミック、メモリオーダリング、およびテストに関するいくつかのこと。

まず、その例は図解です。あなたはそれを読んでそれについて考えることになっています。現実の世界では、新しいスレッドを開始するオーバーヘッドは、開始write_x_then_yされるずっと前read_y_then_xに実行が終了することを意味します。したがって、これら2つのスレッドを繰り返し起動するテストプログラムでは、実際には並べ替えは発生しません。マルチスレッドコードをテストする素晴らしい世界へようこそ!

次に、考慮すべき2つの並べ替えの問題があります。

まず、コンパイラーは、ソースコードが使用するのとは異なる順序で物事を格納または読み取るコードを生成できます。これは、マルチスレッドがない場合の有効な最適化であり、重要なものです。一方、複数のスレッドを導入すると、ストアの順序と読み取りの順序が重要になる可能性があります。その結果、新しいC ++メモリモデルは、ストアとロードを移動できないタイミングを指定します。特に、アトミックアクセス間で移動することはできません。これにより、推論できる固定点が得られます。このアトミックストアを実行する前に、この非アトミックストアを実行したので、コンパイラが最初のストアを2番目のストアの前に実行することを知っています。

第二に、ハードウェアストアとロードを並べ替えることができます。これは通常、プロセッサのキャッシング戦略の結果であり、「可視性」と呼ばれます。あるスレッドで変数に加えられた変更は、最初のスレッドがその変数に書き込んだ後にその変数を読み取る別のスレッドに必ずしも表示されるとは限りません。これは、2つのスレッドがそれぞれ独自のキャッシュを持つ2つの別々のプロセッサで実行できるためです。新しい値がメインメモリに書き出されていない場合、または他のプロセッサのキャッシュに古い値がある場合、2番目のスレッドは変更を認識しません。アトミックは、値が表示されるタイミングに関するルールを提供します(これは、書き込みをキャッシュからメインメモリにフラッシュする必要がある場合、および読み取りをキャッシュではなくメインメモリに送信する必要がある場合に変換されます[単純化しすぎていますが、わかります])。それがこの例の内容です。と、@Michaelが言ったように、値を表示する必要がないからといって、表示できないという意味ではありません。一部のプロセッサには、この種のことを可能にする弱いメモリモデルがあり、動作を分析するときに速度が向上し、明らかに複雑になる可能性がありますが、そうでないプロセッサもあります。x86は後者のカテゴリに分類されます。可視性の制約を弱くしても、実行するほとんどすべてのことは順次一貫性があります。

于 2012-09-01T12:11:34.077 に答える
0

xとyを並べ替えることができるからといって、コンパイラが不確定な動作を生成する必要はありません。

x.store(false, memory_order_relaxed); // redundant store
y.store(true, memory_order_relaxed);
x.store(true, memory_order_relaxed);

xが真であると見なす場合、yは真でなければならないようです。ただし、コンパイラはそれらを並べ替えることを選択できます

x.store(false, memory_order_relaxed); // redundant store
x.store(true, memory_order_relaxed);
y.store(true, memory_order_relaxed);

それは選択しますか?この場合、おそらくそうではありません。コンパイラーでは、xyxパターンで最適なコードを生成するのは簡単です。ただし、より複雑なケースでは、より多くのことが行われているため、順序を変更すると、コンパイラーがより多くの値をレジスターに収めることができる場合があります。

于 2013-09-03T05:28:49.113 に答える
0

メモリモデルは、可能なことを説明するだけであり、順序付けが実際に行われるという保証はありません。コンパイラとハードウェアの両方に依存します。許可された動作を徹底的に調査したい場合は、CDSCheckerなどのツールを使用してください。

于 2013-09-03T05:51:32.503 に答える