5

マルチスレッドプログラミングに関する多くのリソースを調べていると、通常、揮発性指定子への参照が発生します。このキーワードの使用は、少なくともC / C ++とJava(バージョン1.4以前)で複数のスレッド間の同期を実現するための信頼できる方法ではないことは明らかです。ウィキペディアがこの指定子の典型的な使用法として(方法を説明せずに)リストしているものは次のとおりです:-

  1. メモリマップドデバイスへのアクセスを許可する
  2. setjmpとlongjmpの間の変数の使用を許可する
  3. シグナルハンドラーでの変数の使用を許可する
  4. ビジーウェイト

上記の使用法でこの指定子の役割を理解し始めることができますが、これらの各領域をまだ完全に理解していないため、これらの使用法のそれぞれでこの指定子がどのように動作するかを正確に理解できません。

誰かが説明できますか?

4

8 に答える 8

12

あなたの質問は技術的には「ワームの缶」として知られています!c / c ++の場合(javaについてはコメントできません)
volatileは、コンパイラーに「これを最適化しないでください」という指示として非常に大まかに要約できますが、専門家の間では、かどうかについて多くの議論があります。それ
はa)カーネルレベルのコードにまったく役立ちます <-フィードバックに基づいて明確化された編集
b)ほとんどのコンパイラによって正しく実装されています。

また、マルチスレッドプログラミングには使用しないでください。理由については非常に適切な説明があります。

=編集=興味深いことに、その価値について。デニス・リッチーは、ここに(constと同様に)詳細を含めることに反対しました

于 2009-06-30T11:37:39.187 に答える
7

これらの使用例に興味があるので、最初の使用例を説明します。これはac/c ++の観点から当てはまることに注意してください。一般に、c / c ++とjavaの揮発性はまったく異なる場合に使用されると思いますが、Javaでどのように機能するかはわかりません。

メモリマップドデバイスは、プロセッサが特別なバスを介さずにメモリと同じ方法で通信する周辺機器です。

メモリマップされたタイマーを備えた小さなライトがあるとします。メモリアドレスに1を書き込むことでライトをオンにすると、内部タイマーが5秒間カウントダウンし、ライトをオフにしてメモリ位置を0にリセットします。現在、特定のイベントの後にライトをオンにする必要があるACプログラムを開発しています。 、および場合によっては、カウンターが期限切れになる前にオフにします。通常の変数(このタイプのアプリケーションのポインターまたは参照の傾向があります)を使用してそのメモリー位置に書き込む場合、コンパイラーの最適化が原因で問題が発生する可能性があります。

それほど多くの変数を使用しておらず、その値を使用する他の変数なしでライトをオンにし、オフにした直後に、コンパイラが最初の割り当てを完全に削除する場合もあれば、削除する場合もあります。プロセッサレジスタの値を維持するだけで、メモリに書き込むことはありません。どちらの場合も、メモリが変更されていないため、ライトwoudlがオンになることはありません。

次に、ライトの状態を確認してオンになっている別の状況を考えてみます。ここで、値はデバイスのメモリから抽出され、プロセッサレジスタに保持されます。さて、数秒後、ライトは自動的に消えます。その後まもなく、ライトを再びオンにしようとしますが、そのメモリアドレスを読み取り、それ以降変更していないため、コンパイラは値がまだ1であると想定し、実際には0になっていますが、変更することはありません。

揮発性キーワードを使用することにより、コードをマシンコードに変換するときにコンパイラーがこれらの仮定を行わないようにし、プログラマーが記述したとおりにすべての特定の操作が厳密に実行されるようにします。これは、メモリの場所がプロセッサによって厳密に変更されないため、メモリマップドデバイスにとって不可欠です。これらと同じ理由で、共有メモリを備えたマルチプロセッサシステムでは、共通のメモリスペースで動作する場合に同様の方法が必要になることがよくあります。

于 2009-06-30T18:13:56.087 に答える
4

Herb SutterによるこのDDJの記事は非常に興味深いものであり、特にC ++、Java、C#.NETで揮発性がどのように扱われるかがわかりました。

Dr.Dobbs揮発性vs.揮発性

于 2009-06-30T13:42:03.757 に答える
2

BrianGoetzが著書「JavaConcurrencyinPractice」(JCIP)で述べているように、揮発性変数はJavaで役立ちます(少なくとも動作が変更されたJava 5.0以降)。

変数への更新が他のスレッドに予測どおりに伝播されるようにするため

明示的な同期でもこれを実現できますが、値をロックしたくない場合がよくあります。ダブルチェックロックはこの典型的な例です(ウィキペディアからコピー):

// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (null == helper)
                    helper = new Helper();
            }
        }
        return helper;
    }

    // other functions and members...
}

ヘルパーが揮発性でない場合、これは機能しません。

揮発性変数を使用して、java.util.concurrent.ConcurrentHashMap(ロックなしの同時更新とアクセスをサポートします。揮発性の使用についてはJDKソースコードを参照してください)などの非ロック同時データ構造を実装することもできます。

JCIPには、ダブルチェックされたロック、揮発性変数、および一般的なJavaの並行性についてのすばらしい議論があります。Joshua Blochによる「EffectiveJava」、第2版も読む価値があります。

また、アトミック変数は、java.util.concurrent.atomicパッケージのJavaでサポートされていることに注意してください。これらにより、揮発性変数と同様の方法でスレッド/プロセッサ間で値の変更を表示できますが、「比較と設定」操作を実行することもできます。つまり、追加の種類の同時操作をロックせずに安全に実行できます。

于 2009-06-30T21:28:10.480 に答える
2

ここに良い説明があります:http://en.wikipedia.org/wiki/Volatile_variableしかし、少し単純化されているので、変数が他の誰かによってアクセスされていないことを想定してはならず、それを最適化することは致命的であるとコンパイラに伝えますレジストラに追加し、実際のストレージではなく、レジスタのみを更新します。

于 2009-06-30T11:26:10.630 に答える
1

volatileキーワードはずっと前にCで登場しました。基本的には、変数が明示的に変更されなかった場合、変数がまったく変更されなかったと想定するコンパイラーの最適化を「オフ」にすることです。当時の主なユーティリティは、割り込みハンドラーによって変更される変数を宣言することでした。たとえば、マウスカーソルの位置を含むグローバル変数に1回(80年代後半)使用しました。位置は割り込みによって変更されました。揮発性がないと、コンパイラが変数アクセスを最適化して必要ないと考えたため、メインプログラムがその変更を検出しない場合がありました。

今日、これらの使用法は一般に廃止されています(低レベルのOSコードを記述しない限り)が、揮発性が役立つまれな状況がいくつかあります(実際、非常にまれです-たとえば、私はおそらく最後にそれを使用しませんでした7年間)。

ただし、マルチスレッドプログラミングの場合は完全に推奨されません。問題は、スレッド間の同時アクセスを保護せず、同じスレッドでの「更新」を妨げる最適化のみを削除することです。マルチスレッド環境での使用を目的としたものではありません。Javaを使用している場合は、同期を使用してください。C ++を使用している場合は、pthreadやBoost.Threadsなどの同期ライブラリを使用します(または、可能であれば、新しいC ++ 0Xスレッドライブラリを使用します)。

于 2009-06-30T19:35:36.310 に答える
0

私がC++を実行してからしばらく経ちましたが、その言語でのvolatineの定義を本当に思い出せません。しかし、Java言語仕様では、volatileの目的は、変数へのマルチスレッドアクセスを容易にすることであると具体的に述べています。引用:「フィールドは揮発性と宣言される場合があります。その場合、Javaメモリモデル(§17)により、すべてのスレッドが変数の一貫した値を確認できます。」彼らはさらに、揮発性の値への参照は、コードで指定された順序で満たされることが保証されていると言います。つまり、iとjを揮発性と宣言してから、「++ i; ++ j」と書くと、i実際、常にjの前にインクリメントされます。

Javaでvolatileを使用したことを思い出したのは、あるスレッドがキャンセルフラグを設定し、別のスレッドが大きな操作をループし、ループを通過するたびにキャンセルフラグをチェックしたときだけでした。これは確かに私が期待したように機能しました。

「揮発性」の有用性は非常に限られていることに同意します。ほとんどのマルチスレッドは、ある時点で「同期」する必要があります。しかし、「制限付き」と「なし」は同じものではありません。余弦関数は、ほとんどのビジネスアプリケーションでも非常に限られた有用性しかありません。しかし、あなたがそれを必要とするとき、すごい、それは多くのトラブルを救います。

于 2009-06-30T17:30:14.247 に答える
-1

揮発性変数は、その変数に多くのスレッドがアクセスでき、すべての命令でコードがその変数の更新された値を取得する必要がある場合に使用する必要があります。

コンパイラは通常、コードを最適化し、変数をレジスタに格納します。誰も更新していないことを確認した場合、毎回メモリから取得するのではありません。

ただし、volatileを使用することで、コンパイラーに毎回更新された値を取得させることができます。

于 2009-06-30T11:31:23.583 に答える