1

次の構造は、foo の要素が適切に配置され、サイズが適切に設定されているため、単語のティアリングが発生しないと仮定すると、スレッドセーフですか? そうでない場合、なぜですか?

注: 以下のコードは、私がやりたいことのおもちゃの例であり、実際の現実のシナリオではありません。明らかに、私の例で観察可能な動作をコーディングするより良い方法があります。

uint[] foo;
// Fill foo with data.

// In thread one:
for(uint i = 0; i < foo.length; i++) {
     if(foo[i] < SOME_NUMBER) {
         foo[i] = MAGIC_VAL;
     }
}

// In thread two:
for(uint i = 0; i < foo.length; i++) {
     if(foo[i] < SOME_OTHER_NUMBER) {
         foo[i] = MAGIC_VAL;
     }
}

これは一見すると明らかに安全ではないように見えるので、安全であると考える理由を強調します。

  1. foo の要素を変更しないか、MAGIC_VAL に設定するかの 2 つのオプションしかありません。
  2. 更新中にスレッド 2 が中間状態の foo[i] を検出した場合、発生する可能性があるのは次の 2 つだけです。中間状態が <であるSOME_OTHER_NUMBERか、そうでないかです。< の場合SOME_OTHER_NUMBER、スレッド 2 も MAGIC_VAL に設定しようとします。そうでない場合、スレッド 2 は何もしません。

編集: また、foo が long や double などの場合はどうすればよいでしょうか? アラインメントなどは、foo の 1 つの要素を更新しても他の要素に影響を与えないようなものであるとまだ想定しているかもしれません。また、この場合のマルチスレッドの要点はパフォーマンスであるため、どのタイプのロックもこれを無効にします。

4

5 に答える 5

4

最新のマルチコアプロセッサでは、コードはメモリバリアなしでは(少なくともほとんどの言語で)スレッドセーフではありません。簡単に言えば、明示的な障壁がなければ、各スレッドはキャッシュからのfooの異なる完全なコピーを見ることができます。

2つのスレッドがある時点で実行され、その後のある時点で3番目のスレッドがfooを読み取ったとすると、完全に初期化されていないfoo、他の2つのスレッドのいずれかのfoo、または両方、CPUメモリキャッシングで何が起こったかによって異なります。

私のアドバイス-並行性について「賢く」なるのではなく、常に「安全」になるようにしてください。スマートは毎回あなたを噛みます。壊れたダブルチェックのロック記事には、メモリバリアがない場合にメモリアクセスと命令の並べ替えで何が起こるかについての目を見張るような洞察があります(特にJavaとその(変更する)メモリモデルについては、どの言語でも洞察力があります)。

ショートカットバリアには、言語で指定されたメモリモデルを実際に上回っている必要があります。たとえば、Javaでは、変数にvolatileのタグを付けることができます。これは、アトミックな代入があると文書化されている型と組み合わせると、非同期の代入とフェッチをメインメモリに強制的に渡すことができます(したがって、スレッドはキャッシュされたコピーを監視/更新しません) 。

于 2009-01-15T20:35:06.457 に答える
1

コンペアアンドスワップ操作を使用すると、これを安全かつロックレスで実行できます。あなたが持っているものはスレッドセーフに見えますが、コンパイラは状況によっては変更されていない値の書き戻しを作成する可能性があり、それにより一方のスレッドがもう一方のスレッドを踏むことになります。

また、このように両方のスレッドが同じ連続したメモリに書き込むと、CPUのキャッシュ内でMESI遷移のストームが発生し、それぞれが非常に遅いため、これを実行すると思ったほどのパフォーマンスが得られない可能性があります。マルチスレッドメモリの一貫性の詳細については、UlrichDrepperの「すべてのプログラマーがメモリについて知っておくべきこと」のセクション3.3.4を参照してください。

于 2009-01-15T20:34:19.657 に答える
0

各配列要素への読み取りと書き込みがアトミックである場合 (つまり、前述のように単語のティアリングがなく適切に配置されている場合)、このコードに問題はありません。がまたはfoo[i]のいずれかより小さい場合、少なくとも 1 つのスレッド (おそらく両方) がそれをある時点で に設定します。それ以外の場合はそのままです。アトミックな読み取りと書き込みでは、他の可能性はありません。SOME_NUMBERSOME_OTHER_NUMBERMAGIC_VAL

ただし、状況はより複雑であるため、非常に注意してfoo[i]ください。ループごとに 1 回だけ読み取られ、ローカル変数に格納されていることを確認してください。同じ反復中に複数回読み取ると、一貫性のない結果が得られる可能性があります。コードにわずかな変更を加えただけでも、すぐに競合状態で安全でなくなる可能性があるため、大きな赤い警告サインでコードについて強くコメントしてください。

于 2009-01-15T20:01:08.710 に答える
0

結果に関係なく、2 つのスレッドが同時に同じ変数にアクセスしている状態になるべきではありません。あなたが与える例は単純化されすぎており、大多数の複雑なサンプルにはほとんどの場合、それに関連する問題があります.. ...

覚えておいてください:セマフォはあなたの友達です!

于 2009-01-15T20:02:14.293 に答える
0

その特定の例はスレッドセーフです。

ここに実際に関与する中間状態はありません。その特定のプログラムは混乱しません。

ただし、アレイにはミューテックスをお勧めします。

于 2009-01-15T20:04:16.190 に答える