10

複数のスレッドによってアクセスされているミューテックスによって保護されていない変数 (おそらく volatile とマークされている) が最終的に一貫するという一般的な標準 (ISO C または C++、または POSIX/SUS 仕様のいずれか) による保証はありますか?割り当てられている場合は?

具体的な例として、2 つのスレッドが変数 v を共有し、初期値がゼロであるとします。

Thread 1: v = 1

Thread 2: while(v == 0) yield();

Is thread 2 guaranteed to terminate eventually? Or can it conceivably spin forever because the cache coherency never kicks in and makes the assignment visible in thread 2's cache?

I'm aware the C and C++ standards (before C++0x) do not speak at all about threads or concurrency. But I'm curious if the C++0x memory model, or pthreads, or anything else, guarantees this. (Apparently this does actually work on Windows on 32-bit x86; I'm wondering if it's something that can be relied on generally or if it just happens to work there).

4

6 に答える 6

12

それはあなたのアーキテクチャに依存するでしょう。メモリ書き込みが他のスレッドに見えるようにするために明示的なキャッシュ フラッシュまたはメモリ同期を要求することは珍しいことですが、それを妨げるものは何もありません。状態が確実にフラッシュされるように実行する必要があります。

ミューテックスのようなスレッド同期プリミティブは、必要に応じて必要な作業を実行しますが、一貫性を気にせずに状態が確実に見えるようにすることだけが必要な場合は、通常、実際にはスレッド同期プリミティブは必要ありません。同期/フラッシュ命令だけで十分です。

volatile編集:キーワードについてまだ混乱している人へ-volatileコンパイラーが明示的にデータをレジスターにキャッシュするコードを生成しないことを保証しますが、これは読み取りと書き込みを透過的にキャッシュ/再注文するハードウェアを扱うことと同じではありません。たとえば、thisまたはthis、またはthis Dobbs の記事、またはthis SO question への回答を読むか、Cell のような一貫性の低いメモリ アーキテクチャをターゲットとするお気に入りのコンパイラを選択し、テスト コードを記述して、コンパイラが生成するものとあなたが生成するものを比較してください。 d は、書き込みが他のプロセスから見えるようにするために必要です。

于 2010-06-24T21:52:12.723 に答える
5

関連するセクションを正しく理解していれば、C++0X はスタンドアロン変数や揮発性変数 (揮発性はその用途向けに設計されていません) に対してもそれを保証しませんが、保証できるアトミック型を導入します。 (ヘッダーを参照<atomic>)。

于 2010-06-25T09:27:38.150 に答える
3

これは潜在的なデータ競合です。

POSIX スレッドに関しては、これは UB です。C++ と同じだと思います。

実際には、どのように失敗するか想像できません。

于 2011-10-02T05:23:20.077 に答える
3

まず、揮発性とマークされていない場合、コンパイラが一度しかロードしない可能性があります。したがって、メモリが最終的に変更されるかどうかに関係なく、コンパイルがメモリを設定するという保証はありません。

「ミューテックスなし」と明示的に言っているため、pthreads は適用されません。

さらに、C++ にはメモリ モデルがないため、ハードウェア アーキテクチャに依存します。

于 2010-06-24T21:51:41.353 に答える
2

スレッド 2 は最終的に終了することが保証されていますか? それとも、キャッシュ コヒーレンシが開始されず、割り当てがスレッド 2 のキャッシュに表示されるため、永遠にスピンし続ける可能性がありますか?

変数が揮発性でない場合、保証はありません。C++0x より前の標準では、スレッドについて何も述べていません。変数は揮発性ではないため、読み取り/書き込みは目に見える副作用とは見なされないため、コンパイラは不正行為を行うことができます。C++0x 以降、これは競合状態であり、未定義の動作であると明示的に述べられています。

変数が揮発性である場合、読み取り/書き込みが発生し、コンパイラが他の揮発性メモリ アクセスに関して並べ替えられないことが保証されます。(ただし、これだけでは、CPU がこれらのメモリ アクセスを並べ替えないことを保証するものではありません。コンパイラが並べ替えないというだけです)。

ただし、他の不揮発性アクセスに関して並べ替えられないという保証はないため、期待した動作が得られない可能性があります。特に、「保護」しようとしている while ループの後の命令の一部は、コンパイラがそうするのが安全 (かつ有益) であると判断した場合、ループの前に移動することがあります。しかし、この分析を実行する際には、他のスレッドで何が起こっているかではなく、現在のスレッドのみを調べます。

いいえ、一般的には、volatile. 可能性があり、おそらく頻繁にそうなるでしょうが、常にそうであるとは限りません (ループの後に何が起こるかによって異なります)。それは、コンパイラが最適化をどこまで進めるかによって異なります。しかし、コードを破るのに十分な距離まで行くことは許されています。だからそれに頼らないでください。このようなことを同期したい場合は、メモリバリアを使用してください。それが彼らの目的です。(そして、それを行うと、もう必要ありませんvolatile

于 2010-06-25T09:57:00.570 に答える
0

最終的にはどのプラットフォームでも機能すると思いますが、遅延が発生する可能性はありません。

しかし、正直なところ、イベントを待つポーリングを行うのは本当に悪いスタイルです。あなたが行ったとしても、yieldあなたのプロセスは何もせずに何度も何度も再スケジュールされます。

両方がアクセスできる場所に変数を配置する方法をすでに知っているので、適切なツールを使用して、リソースを消費しない待機を実行してみませんか?pthread_mutex_tとのペアはpthread_cond_t完全にトリックを行う必要があります。

于 2010-06-25T07:55:34.190 に答える