4

私はこの文を見ました:

一般的なルールは、複数のスレッド間で共有する必要があるプリミティブ型の変数がある場合、それらの変数を 揮発性として宣言することです

この記事とこの文から:

一般に、非同期に日付が付けられない可能性のあるデータは、揮発性であると宣言する必要があります。

このページから、この導入されたルールを考慮して、データへの非同期アクセスが存在するにもかかわらず、データの揮発性が実際には使用されないことを宣言している場合、またはそのような例外的なケースとルールがない場合の例を教えてください。厳しいです。

4

8 に答える 8

12

その記事が公開されたときのことを覚えています。その後、comp.lang.c++。moderatedで続いた無限の議論を覚えています。

IIRC、Andreiはvolatileキーワードをハイジャックして、さまざまな関数のオーバーロードを区別するために使用します。(別のそのようなアイデアについては、Scott Meyersによるこの記事を参照してください。)オブジェクトへの保護されたアクセスと保護されていないアクセスを台無しにした場合にコンパイラーがキャッチできるという点で、彼の仕事は素晴らしいです(コンパイラーがキャッチするのと非常によく似ています)。定数を変更する)。しかし、それがあなたを助けるという事実以外に、それはオブジェクトへの同時アクセスを実際に保護することとは何の関係もありません。

問題は、90%の人が記事を一目見ただけで、同じ記事の「スレッド」だけが表示volatileされることです。彼らの知識に応じて、彼らはスレッドに良い間違った結論を引き出すvolatileか(あなたはそうしているようです)、他の人に間違った結論を引き出すように導くために彼に怒鳴ります。
実際に記事をよく読んで、彼が実際に何をしているのかを理解できる人はほとんどいないようです。

于 2010-11-12T20:43:13.937 に答える
11

マルチスレッドがあまり得意ではないため、実際の非同期アクセスシナリオについて話すことはできませんが、volatile修飾子が行うことはコンパイラーに指示することです。

「聞いてください、これいつでも変わる可能性があるので、キャッシュしたり、レジスターに入れたり、そのようなクレイジーなことをしたりしないでくださいね」

非同期書き込みから保護するのではなく、変数が外力によって変更される可能性がある場合に無効な最適化を無効にするだけです。

編集:潜在的な例として、マルチスレッドを含まない(ただし、非常に複雑なコードを含む;)例として、volatileが重要な場合を次に示します。

volatile bool keepRunning = true;
void Stuff() {
    int notAPointer = 0;

    notAPointer = (int)(&keepRunning); //Don't do this! Especially on 64-bit processors!

    while(keepRunning) {
        *(bool*)(notAPointer) = false;
    }

    printf("The loop terminated!");
}

その揮発性修飾子がないと、コンパイラーは「ねえ、keepRunningは変更されないので、それをチェックするコードを生成する必要さえありません!」となる可能性がありますが、実際には秘密裏に変更しているだけです。

(実際には、これは最適化されていないビルドでも機能する可能性があります。また、コンパイラーがスマートで、ポインターが取得されていることに気付いた場合も機能する可能性があります。ただし、原則は同じです)

于 2010-11-12T20:26:14.687 に答える
3

これを読んでください。Volatileはマルチスレッドとは何の関係もありません。

于 2010-11-12T20:28:20.323 に答える
2

マイクの答えをフォローアップするには、次のような場合に役立ちます(この例では複雑さを回避するために使用されるグローバル変数)。

static volatile bool thread_running = true;

static void thread_body() {
    while (thread_running) {
        // ...
    }
}

static void abort_thread() {
    thread_running = false;
}

複雑さによってthread_bodyは、コンパイラーはthread_runningスレッドの実行開始時にの値をレジスターにキャッシュすることを選択する場合があります。つまり、値がfalseに変更されても通知されません。 すべてのループでvolatile実際の変数をチェックするコードをコンパイラーに強制的に発行させます。thread_running

于 2010-11-12T20:29:20.073 に答える
2

私はかなり厳密ですが非常に有用なルールを提案します:あなたが何をするのかを正確に理解していないvolatileなら、それを使わないでください。代わりに、を使用してlockください。何をどのように使用するかを正確に理解していない場合はlock、マルチスレッドを使用しないでください。

于 2010-11-12T20:29:23.560 に答える
2

理論的には、これらのルールは完全に正しく、変数が 2 つのスレッドによってアクセスされるたびに volatile が必要であると言えます。(ミューテックスを使用している場合でも、コンパイラの最適化を妨げないためです。)しかし実際には、コンパイラは、変数が特定の関数の外部で変更される可能性がある状況を十分に認識できるため、その値をレジスタにキャッシュする必要はありません。ほとんどの場合、volatile は必要ありません。

さまざまな状況でアセンブラーの出力を調べて、MSVC でいくつかのテストを行ったことがあります。変数がキャッシュされないようにするために必要だったのは、別の関数が同じ変数に書き込むか、そのアドレスを取得することだけでした。「プログラム全体の最適化」がオンになっていない限り、グローバル変数は最適化されませんでした (そのため、コンパイラーは変数が他の翻訳単位で変更されていないことを確認できます)。

于 2010-11-12T21:42:22.023 に答える
0

最初にリンクした記事を真剣に受け止める前に、別の. その後のUsenet への投稿で、Andrei は後に元の記事の一部が明らかに間違っていたことを認め、この記事はより現実的な見方を示していると指摘しました (ただし、彼がそこに付けたリンクは古くなったようです)。 )。

于 2010-11-13T00:00:39.097 に答える
0

同様に、C++ 標準では volatile がどのように機能するかを指定していないため、特定のコンパイラが特定のプラットフォームに対して何を行うかを調べる必要があります。また、揮発性は少し微妙であり、それが何をするかは、コンパイラとターゲット プラットフォームのメモリ モデルに依存します。ロックはより直感的に使用できます。

于 2010-11-12T20:35:25.530 に答える