4

更新:私は別の形式でこの質問をしました(以下を参照)、そしてそれは建設的ではなかったために閉じられました。答えが私が尋ねたものを正確に扱っていたので(そして私の問題を解決したので)ちょっと残念ですが、私はここで新しいので、私は確かにそれをより建設的にするためにもう一度試みます。

私はWindows7のVC++で作業しています。マルチスレッドプログラムは、1つのスレッドの変数に値を割り当て、イベントオブジェクトを介して、ブロックされている別のスレッドに信号を送信し、その信号を待ちます。コンパイラによって提供される最適化などの理由により、一方のスレッドが(ブロッキングメカニズムを介して)もう一方のスレッドが試行しないことが確実な場合でも、一方のスレッドによって変数に割り当てられたデータが実際にもう一方のスレッドで利用できるという保証はありません。データが変数に割り当てられた後までアクセスします。たとえば、値がCPUレジスタにあり、そのレジスタが他の何かに必要になるまでそこにとどまる場合があります。これにより、値がそのレジスタに入れられた直後に再び必要になった場合に、メモリからの不要なロードを回避できます。不運にも、つまり、メモリ内の対応する場所は、新しい値が割り当てられる前に保持していた最後の値を保持し続けます。したがって、他のスレッドがブロックを解除し、変数の値を保持しているメモリにアクセスすると、最近割り当てられた値ではなく、古い値。

問題は、あるWindowsスレッドが変数に割り当てた値のメモリにストレージを強制し、別のスレッドが後でそれらに確実にアクセスできるようにする方法です。いくつかの答えがあるかもしれませんが、この質問が閉じられる前に提供されたものは、私が必要としたものに最も適していると思われました。これは、以前は聞いたことのないプログラミング構造である「メモリフェンス」の使用でした。フェンスに遭遇した後、メモリへの保留中の書き込みが完了したことが保証されます。(これは、フェンスが「書き込み」フェンスの場合です。「読み取り」フェンスを使用してメモリからの読み取りを強制でき、「読み取り/書き込み」フェンスを使用して両方を実行できます。Windowsでは、VC++内で3つすべてを非常に簡単に使用できます。プログラム。)

ちょっとした落とし穴の1つは、Windowsフェンス(別名「メモリバリア」)が保証をグローバルストレージにのみ適用し、ローカルストレージには適用しないことです(該当するMSDNページで説明されている理由により)。

ここでのメモリフェンスの仕組みに関する私の解釈が正しくない場合(そしてモデレーターがこの質問を再び開く場合)、コメントで説明されているのを見てうれしく思います。結局のところ、私が知らなかったことを認めるほど謙虚でなかったかどうかは尋ねません。(モデレーターが再度開いていないのに、何か問題があることがわかった場合は、メールを送って知らせてください。ブログでこのディスカッションを継続できるようにサポートさせていただきます。もし、するなら。)

元のバージョン
スレッド間でデータを共有するための良い方法は何ですか?

先ほど、私にとって大きな学習体験を開く変数について質問しました。volatileとりわけ、私は正しい質問をしていないことに気づきました。これが悪いスタックオーバーフローのエチケットではないことを願っていますが、私の根本的な問題に対処する新しい質問をここに作成する必要があると思います:

Visual C ++プログラムには、AとBの2つのスレッドがあります。Bはブロックされ、Aからの信号を待機します。Aはいくつかの変数を設定します。次に、AはBに信号を送り、Aによって設定された変数を読み取ります。Aによって設定された変数の一部は、CPUレジスタにのみ存在する可能性があるため、実際にはメモリに書き戻されない可能性があります。

スレッドBが、スレッドAによって以前に設定された変数を読み取るときに、スレッドAが設定した値を読み取ることを確認するための良い方法は何ですか?

4

4 に答える 4

3

x86 アーキテクチャーでは、優れたライブラリーを使用する際に心配することはあまりありません。

ミューテックス (boost::mutex など) を使用して共有データへのアクセスを保護し、ミューテックスの実装者がそれを正しく行った場合、メモリ バリア ( Memory Barriers @ MSDN ) を使用して、キャッシュが確実にアクセスされるようにします。メモリにフラッシュされます。

独自の同期コードを作成する必要がある場合は、それにメモリ バリアを追加します。

于 2012-05-22T17:20:31.600 に答える
1

あなたがコメントで述べたように、私の特定の問題は、ブロックが解除されると、スレッドが他のスレッドによって共有場所に書き込まれた値に実際にアクセスできることを保証できることです

あなたの質問への答えは簡単だと思います.最新のメモリ値を確実に読み取るために使用することができます_ReadWriteBarrier()._WriteBarrier

私の知る限り、C/C++ ではメモリ バリア セマンティクスが保証されていないことに注意してくださいvolatileそのvolatileため、これらの言語で単純に使用することはできません。メモリバリアは、最新の値を単純に読み取るための方法です。

于 2012-05-22T17:19:38.697 に答える
0

「競合状態を防ぐためにアクセスを同期するにはどうすればよいですか?」他のスレッドからのシグナルを待っている間に各スレッドをブロックすることで、それを管理できたと思います。私の特定の問題は、ブロックが解除されると、スレッドが他のスレッドによって共有された場所に書き込まれた値に実際にアクセスできることを保証できることです。

はい、正確に。問題は、あるスレッドによって設定されたシグナルを待機するだけでは、そのスレッドの他のアクティビティが現在のスレッドから確実に見えるようにするのに十分ではないということです。スレッドが変数を設定し、シグナルをトリガーすると、シグナルを待機しているスレッドが変数にアクセスできますが、まったく異なる値を取得できます。

私は現在、このトピックに関するAnthony Williams の著書C++ Concurrency in Actionを楽しんでいます。答えは、std::atomic メモリ順序を正しく使用することにあるようです。次に例を示します。

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

-- thread 1 --
i.store(100,std::memory_order_relaxed);
signal.store(true,std::memory_order_release);

-- thread 2 --
while(!signal.load(std::memory_order_acquire));
assert(i.load(std::memory_order_relaxed) == 100);

2 番目のスレッドがシグナルを確認すると、memory_order_release で実行されたストアと memory_order_acquire で実行されたロードの間に関係が確立され、i へのストアが 2 番目のスレッドで表示されることが保証されます。したがって、アサーションは保持されることが保証されます。

一方、厳密でないメモリ順序を使用すると、保証は得られません。

-- thread 1 --
i.store(100,std::memory_order_relaxed);
signal.store(true,std::memory_order_relaxed);

-- thread 2 --
while(!signal.load(std::memory_order_relaxed));
int i2 = i.load(memory_order_relaxed);
// No guarantees about the value loaded from i!

または、データ競合がない限り、シーケンシャルの一貫性を保証するデフォルトのメモリ順序を使用することもできます。

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

-- thread 1 --
i = 100;
signal = true;

-- thread 2 --
while(!signal);
assert(i == 100);
于 2012-05-22T18:15:34.550 に答える
0

これは、「オブジェクト指向プログラムを作成する良い方法は何か」と尋ねるようなものです。その質問を除いて、私は「良い本を読んでください」と言いますが、これに関しては、悪いパラダイムに関する良い本は実際にはありません. 多くのマルチスレッド プログラミングは、共有データを適切に使用するのではなく、使用を最小限に抑えることに基づいています。

したがって、私の提案は次のとおりです。

1) この方法で 2 つのスレッドが相互に通信する必要がないように設計します。ここでは、真に独立した 2 つのスレッドというよりも、1 つの手続き型スレッドが進行しているように思えます。

2) プロセス内またはプロセス間にサービス指向アーキテクチャを実装します。ポーリングされるグローバル変数の使用に依存するのではなく、一時的な要求/応答パターンですべての共有データが発生するようにします。A が設定し、B に読み取るように指示するこれらすべての変数は、「クライアント」A が「サーバー」B に送信する「要求」のように聞こえます。

3) ライブラリをインストールして学習することに問題がない場合は、ZMQ をお勧めします。私はそれで良い経験をしてきました. 他に何もなければ、ドキュメントは、代わりにそれらを含まないパターンのスレッド間で共有データをキャッシュすることについて考える良い方法を提供するかもしれません.

于 2012-05-22T17:13:13.583 に答える