9

私が揮発性について読んだことはすべて、決して安全ではないと言っていますが、それでも試してみたいと思っています.この特定のシナリオが安全でないと宣言されたのを見たことはありません.

メインのシミュレーション スレッドからデータを取得して、シーンをレンダリングする別のスレッドがあります。これには同期がなく、正常に動作します。

問題は、プログラムが終了したときに、レンダラーが無効なメモリを読み取ろうとすることなく、シミュレーション スレッドが安全にクリーンアップできるようになる前に、レンダラーがシミュレーション スレッドからのデータのプルを停止する必要があることです。

これを実現するために、レンダラーをそのスレッドで無限に実行します。

volatile bool stillRendering;

void RenderThreadFunction()
{
    stillRendering = true;

    while(programRunning)
    {
        renderer->render();
    }

    stillRendering = false;
}

メイン プログラム スレッドで、windproc 終了メッセージを受信すると、次のようにします。

void OnQuit()
{
    programRunning = false;
    while(stillRendering)
    {
    }

    delete application;
}

これの目的は、アプリケーションで delete を呼び出す前に、レンダラーがアプリケーションからのデータのプルを停止することを確認することです。

最初に volatile キーワードを使用せずにこれを試したところ、デバッグ モードでは機能しましたが、リリース モードではハングしました。プログラムが stillRendering の値のチェックを停止するような最適化をコンパイラが行ったとします。

volatile を stillRendering に追加すると、これまでテストしたたびに、アプリケーションが正常に終了しました。「programRunning」が揮発性である場合に問題がないように見える理由はわかりません。

最後に、「stillRendering」に volatile を使用することで、プログラムのパフォーマンスがどのように影響を受けるかはわかりません。stillRendering を volatile にすることが OnQuit() のパフォーマンスに影響するかどうかは問題ではありませんが、RenderThreadFunction() のパフォーマンスに影響するかどうかは問題です

4

5 に答える 5

8

一部のコンパイラでは動作する可能性がありますが、これは完全に安全ではありません。基本的に、volatileアタッチされている変数にのみ影響するためRendererThreadFunction、たとえば、 終了 する前にstillRenderingfalseを設定できます。(これは、 とが両方とも揮発性であった場合でも当てはまります。) 問題が発生する確率は非常に小さいため、おそらくテストでは明らかになりません。最後に、VC++ の一部のバージョンでは、C++11 でアトミック アクセスのセマンティクスが提供されます。この場合、コードは機能します。 (もちろん、別のバージョンの VC++ でコンパイルするまでは。)renderer->render();stillRenderingprogramRunningvolatile

ほぼ確実に無視できない時間がかかることを考えると、renderer->render()ここで条件変数を使用しない理由はまったくありません。このような場合に使用するvolatileのは、シャットダウン メカニズムがシグナルによってトリガーされた場合のみです (この場合、タイプは でsig_atomic_tなくboolになりますが、実際にはおそらく違いはありません)。その場合、スレッドは 2 つではなく、レンダラー スレッドとシグナル ハンドラーだけになります。

于 2013-02-25T19:28:48.867 に答える
6

コードをすべてのコンパイラのすべてのアーキテクチャで動作させたい場合は、C++11 アトミックを使用します。

std::atomic<bool> stillRendering;

void RenderThreadFunction()
{
    stillRendering = true;

    while(programRunning)
    {
        renderer->render();
    }

    stillRendering = false;
}

Volatile は、マルチスレッドで使用するように設計されていませんvolatile。コンパイラは、実際には標準により、アクセスを非volatileアクセスで並べ替えることが許可されています。VC++ はvolatile、並べ替えを防ぐために の機能セットを拡張しますが、他のコンパイラはそうではなく、それらのコンパイラでは壊れる可能性があります。

他の人が述べたように、volatile可視性にも影響しません。つまり、キャッシュ コヒーレントでないアーキテクチャでは、フラグが設定されていない可能性があります。x86 はすぐにはキャッシュ コヒーレントでさえありません(書き込みは非常に遅くなります)。そのため、書き込みがさまざまなバッファーを介して送信される間、プログラムは常に必要以上にループすることになります。

C++11 アトミックは、これらの問題の両方を回避します。

OK、これは主に現在のコードを修正し、 を誤用しないように警告することを目的としていましたvolatile。条件変数を使用するという James の提案 (これは単に、あなたが行っていることのより効率的なバージョンです) は、おそらくあなたにとって最良の実際の解決策です。

于 2013-02-25T19:24:14.220 に答える
4

C++11 アトミックが対処する 3 つの問題があります。

まず、スレッドの切り替えは、値の読み取りまたは書き込みの途中で発生する可能性があります。読み取りの場合、元のスレッドが残りの値を読み取る前に、別のスレッドが値を更新する可能性があります。書き込みの場合、別のスレッドが半分書き込まれた値を見ることができます。これは「引き裂き」として知られています。

次に、一般的なマルチプロセッサ システムでは、各プロセッサが独自のキャッシュを持ち、そのキャッシュに対して値を読み書きします。キャッシュとメイン メモリが更新されて、同じ値が保持されるようにすることがありますが、新しい値を書き込むプロセッサがキャッシュをフラッシュし、値を読み取るスレッドがキャッシュからコピーを再ロードするまで、値は異なる場合があります。これは「キャッシュの一貫性」と呼ばれます。

第 3 に、コンパイラはコードを移動し、コードが逆の順序で記述されていても、別の値を格納する前に 1 つの値を格納できます。違いがわかる有効なプログラムを作成できない限り、「as if」ルールの下では問題ありません。

アトミック変数からの読み込みとアトミック変数への保存 (既定のメモリ順序付け) により、3 つの問題すべてが回避されます。変数をマークvolatileしないようにします。

編集:どのアーキテクチャがどの問題を引き起こすかを気にしないでください。標準ライブラリの作成者は、ライブラリの実装が意図しているアーキテクチャに対して既にこれを行っています。ショートカットを探す必要はありません。アトミックを使用するだけです。あなたは何も失うことはありません。

于 2013-02-25T20:45:02.683 に答える
2

キャッシュコヒーレントなアーキテクチャ(x86プロセッサなど)にもよりますが、これは問題なく機能すると思います。2つのスレッドのいずれかが、真のアトミック操作を使用する場合よりも多くの反復で実行される場合がありますが、値の設定と読み取りは片側のみであるため、真のアトミック操作の要件はありません。

ただし、コードを実行しているプロセッサ(コア)が「他のコア」に更新された値を表示させるために特定のキャッシュフラッシュを必要とする場合は、しばらくの間スタックする可能性があります-適切なアトミック更新が必要になります他のプロセッサのキャッシュが無効になっていることを確認してください。

renderer->render()これにはかなりの時間がかかると思いますので、を読んstillRenderingでも全体の実行時間にはあまり影響しないはずです。volatile通常は、「これをレジスターに入れて保管しないでください」という意味です。

(おそらくあなたもそうする必要programRunningがありvolatileます!)

于 2013-02-25T18:53:36.857 に答える
2

volatile をちょうど stillRendering に追加すると、テストするたびにアプリケーションが正常に終了しました

はい、あなたのシナリオは機能します。

volatileスレッド同期に変数を使用する際によくある間違いは、変数に対する操作がアトミックでvolatileあると想定される場合です。そうではありません。

あなたの場合、単一の bool をポーリングして、1 回だけ正確に 0 に変更されるのを待っています。操作がアトミックであることを期待していないようです。一方、単一の をポーリングしていたとしてもint、C++ はその int を変更するスレッドがアトミックにそれを行うことを保証しません。

「programRunning」が揮発性である場合に問題がないように見える理由はわかりません。

それは問題である。 それを作るvolatile

変数を作成するvolatileと、特定のキャッシュの最適化が回避されることが保証されます。これは、必要なことです。

これは、変数揮発性でない場合に同じキャッシュ最適化が保証されるという意味ではありません。あなたは単にコンパイラに決定させているだけです。そして、この特定の時点で、コンパイラーはたまたまあなたに適した決定を下しています。

最後に、「stillRendering」に volatile を使用することで、プログラムのパフォーマンスがどのように影響を受けるかはわかりません。

あなたのパフォーマンスは、これによって悪影響を受ける可能性があります:

while(stillRendering)
{
}

1 つのスレッド (おそらく 1 つの CPU コア全体) に、休むことなく、1 つの変数を無限に読み取るように要求しています。

その while ループにスリープ呼び出しを追加することを検討してください。

于 2013-02-25T19:03:13.790 に答える