2009-06-30 11 views
5

マルチスレッドプログラミングで多くのリソースを使いながら、通常はvolatile指定子への参照が出ます。 このキーワードの使用は、C/C++とJava(バージョン1.4以前)で少なくとも複数のスレッド間で同期を達成するための信頼できる方法ではないことは明らかです。ここ は、一般的な用法として何ウィキペディアリスト(方法を説明なし)この指定子である: -C/C++/Javaでのvolatile指定子の使用

  1. メモリマッピングされたデバイス
  2. へのアクセスを許可するのsetjmpとlongjmp
  3. 間の変数の使用は、信号の変数の使用を許可することができハンドラ待っ
  4. 忙しい

私は上記の用途では、この指定子の役割を見始めることができますが、私はまだ完全understandiを持っていないので、これらの領域のそれぞれについて、これらの使用法のそれぞれでこの指定子がどのように動作するかを理解することはできません。

誰かが説明できますか?

+0

回答ありがとうございます。 上記の用途でどのようにvolatileが動作するかについての入力はありますか? – Ankur

答えて

6

これらの使用例に興味があるので、最初に説明します。これはc/C++の観点からも当てはまりますが、一般的にはc/C++で揮発性の疑いがありますが、Javaは完全に異なる場合に使用されています。

メモリマップデバイスは、プロセッサが特別なバスではなくメモリと同じ方法で通信するペリフェラルです。

メモリマップされているタイマーで少し明るいとします。ライトをオンにするには、メモリアドレス&に内部タイマーが5秒間カウントダウンします。&は、ライトをオフにしてメモリのロケーションを0にリセットします。次に、特定のイベント後にそのライトをオンにする必要のあるACプログラムを開発していますカウンターが期限切れになる前にそれをオフにすることがあります。メモリ位置に書き込むために通常の変数(このタイプのアプリケーションのポインタまたは参照になりやすい)を使用すると、コンパイラの最適化のために間違っている可能性がある多くのことがあります。

多くの変数で作業していない場合、その値を使用して他の変数を使用せずにライトをオンにしてすぐにオンにした場合、コンパイラは最初の割り当てを完全に取り除くか、それ以外の場合は、単にプロセッサレジスタの値をそのまま維持します。&はメモリに書き込まれません。どちらの場合も、メモリは決して変更されていないため、ライトはオンになりません。

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

コードを機械コード&に変換するときに、コンパイラがこれらの前提を行わないようにすることで、プログラマが厳密にすべての特定の操作を確実に実行できるようにします。これは、主にメモリロケーションがプロセッサによって厳密に変更されないため、メモリマップデバイスにとって不可欠です。同じ理由から、共有メモリを備えたマルチプロセッサシステムは、共通のメモリ空間上で動作する場合に、しばしば同様の方法を必要とする。

+0

DBありがとうございます。非常に良い例。 – Ankur

2

ここには良い説明があります:http://en.wikipedia.org/wiki/Volatile_variableしかし、わずかに簡略化されたことは、変数が他の誰かによってアクセスされていないと仮定してはならず、レジスタをレジスタに最適化してレジスタのみを更新する実際のストレージではありません。

+0

CとC++ではJavaとは異なり、驚くべきことでもありません。たとえば、Javaで、コンパイラが、揮発性が単一スレッドによってのみアクセスされ、揮発性でないかのように扱うことができると判断できる場合、 –

+0

正解、私は彼らがウィキペディアでも言及していると思います。しかし、この指定子の目的は、やや同じです。 – Fredrik

-1

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

一般に、コンパイラは、コードやストア変数を更新するのではなく、毎回メモリから取得するのではなく、レジスタとレジスタの変数を最適化します。

volatileを使用すると、毎回更新された値をコンパイラに強制的に渡すことができます。

+1

これは正しくありません。 'volatile'はコンパイラに「コントロールの何かがこの変数を変更して最適化しないかもしれない」と言っているだけです。あなたは本当に何が起こるかについて他の保証はありません。他の回答のリンクは、あなたが記述したように 'volatile'を使うのが間違っている理由を説明しています。 –

+0

cpuキャッシュを検討してください。 C抽象マシンはCPUキャッシュを知らない。そのためには、たとえば、* one * cpuのCPUキャッシュに値が格納されているとします。マルチスレッドアプリケーションの場合、「ストレージ」を意味するようなキャッシュを使用するのは無駄です。 –

+0

@リチャード:合意。volatile指定子は、複数のスレッド間の同期を達成するための手段として使用されるべきではありません。 – Ankur

12

あなたの質問は技術的に「ワームの缶」と呼ばれています! c/C++の場合(私はJavaにはコメントできません)
volatileを非常に大まかに言うと、コンパイラの指示に「最適化しないでください」と言うことができますが、専門家の間では多くの議論があります。それは
a)はAt all useful for kernel level code < -editはEven implemented correctly by most compilers.

またフィードバック
B)に基づいて明らかだかどうかについて、これまでのマルチスレッドプログラミングのためにそれを使用していないとhere's a very good explanation as to why

=編集= 興味深いことに、用それは価値がある。 Dennis Ritchieはそれに反対しています(constと同様)。here

+1

「At all useful」ドキュメントは、Linuxカーネルペーパーにリンクしています。この文書では、Linuxカーネルで使用されているvolatileについて述べています。一番下には、複数のスレッドに関係しない、揮発性の良い使い方があります。揮発性は理にかなっており、同じCPUの割り込みハンドラで変数が更新されているビジーなループのように(コンパイラがvolatileを正しく処理する場合)良いです。コンパイラは、読み込みを最適化すべきかどうかを知ることができません。あなたのポイントは、それが "揮発性"のようなサウンドになります(たとえ私があなたがそれを意図しないかもしれないことを知っていても)完全なごみです:) –

+0

@litb Yep fair point。私はその点を明確にします。私はそれが完全なくそだったとは言わない、ちょうど私はそれがちょうど明確なカットではないことを指摘すると思った。 – zebrabox

+0

まさに私が言うつもりだった。 1つの明確化は、最適化は通常、そこからそれを再利用して、レジスタに値を格納することでした。 Volatileは、コードが各読み取り時にメモリから値を再読み込みし、レジスタ内のキャッシュされた値に依存しないようにします。現在のところ、コンパイラは最適化されたリライトステートメントとその順序を最適化しているため、メモリからの値の再読み込みだけでは不十分です。 – iain

4

このDDJの記事は非常に興味深いものです。特に、C++、Java、C#.NETでどのようにvolatileが扱われていますか?

Dr.Dobbs volatile vs. volatile

+0

ありがとうございます。良いもの。 – Ankur

0

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

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

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

1

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

今日、これらの用途は一般的には廃止されています(低レベルのOSコードを書いていない限り)が、揮発性が有用な珍しい状況があります(まれに、過去7年間)。

しかし、マルチスレッドプログラミングでは完全に推奨されていません。問題は、スレッド間の同時アクセスを保護しないことです。同じスレッドでのリフレッシュを防ぐ最適化を削除するだけです。マルチスレッド環境での使用を目的としていませんでした。 Javaの場合は、synchronizedを使用します。 C++であれば、pthreadやBoost.Threadsなどのシンクロナイゼーションライブラリを使用してください(可能であれば、新しいC++ 0Xスレッドライブラリを使用することをお勧めします)。

2
ブライアン・ゲッツは、彼の著書「実践でのJava並行処理」(JCIP)で述べているよう

揮発性の変数は、(少なくともそのbehaviour changedのJava 5.0以降)Javaのに有用である - テーマに関する基本的な書籍(PG 37):

変数 への更新は、他の スレッド

明示的な同期に予想どおりに伝播されることを保証する

も、これを達成することができますが、多くの場合、我々は常に値をロックする必要はありません。ダブルチェックロックは(Wikipediaからコピーされた)この古典的な例である:

// 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著「Effective Java」第2版も読める価値があります。

また、原子変数はjava.util.concurrent.atomicパッケージでJavaでサポートされていることにも注意してください。これにより、揮発性変数と同様にスレッド/プロセッサ間で値を変更できるようになりますが、「比較および設定」操作を実行できるようになり、いくつかの追加のタイプの並行操作がロックなしで安全に実行できます。

関連する問題