c++ ボラタイル すべてのグローバル変数はvolatile修飾されるべきですか?




volatile ボラタイル (4)

この例では揮発性は必要ありません。 たとえば、some_function()が何かを出力した場合、asmのリストはc ++マシンの観察可能な動作を変更し、標準に違反しているように見えます。

これはコンパイラのバグだと思います。これがGCCアセンブラの出力です。

.cfi_def_cfa_register 5
subl    $24, %esp
.loc 1 67 0
movl    global_value, %eax
addl    $1, %eax
movl    %eax, global_value
movl    global_value, %eax
movl    %eax, (%esp)
call    _Z13some_functioni
.loc 1 68 0
call    _Z3foov
.loc 1 69 0
movl    global_value, %eax
addl    $1, %eax
movl    %eax, global_value
movl    global_value, %eax
movl    %eax, (%esp)
call    _Z13some_functioni
.loc 1 70 0
leave
.cfi_restore 5

global_valueは、予想どおり、関数呼び出しの間に再ロードされます。

また、揮発性スレッドセーフに使用されています。単にv-qualifierは、すべての場合においてスレッドセーフに十分ではありません (アトミック性およびメモリバリアに関して追加の注意が必要な場合があります)。

[編集]: ...それらが繰り返し読み込まれ、読み込みの合間に別のスレッドによって変更される可能性がある場合。 しかし、これは同期ロック(mutexなど)が使用されている場合には当てはまりません。ロックは変数が同時アクティビティによって変更できないことを保証するためです(Rのおかげで)

この例では、正確さのためにglobal_valuevolatile宣言する必要がありますか?

int global_value = 0;

void foo () {
    ++ global_value;
}

void bar () {
    some_function (++global_value);
    foo ();
    some_function (++global_value);
}

私の理解するところでは、 volatileシグナルによって変更可能なマッピングされたメモリや変数へのポインタ (そしてスレッドセーフではない)を「意図している」が、 barが次のようにコンパイルされることは想像しやすいだろう。

push EAX
mov EAX, global_value
inc EAX
push EAX
call some_function
call foo
inc EAX
push EAX
call some_function
mov global_value, EAX
pop EAX

これは明らかに正しくありませんが、 volatileがなくてもC抽象マシンによれば有効であると思います。 私は間違っていますか、それは有効ですか?

もしそうなら、私はvolatileが日常的に見落とされているように思われる。 これは目新しいことではないでしょう。

拡張例

void baz (int* i) {
    some_function (++*i);
    foo ();
    some_function (++*i);
}

int main () {
    baz (&global_value);
}

barが正しいdont-cache-global_value実装にコンパイルされることが保証されていても、 bazは同様に正しいのでしょうか、それとも*i不揮発性の値をキャッシュすることは許されますか。


volatileの唯一の用途は、 longjmp 、シグナルハンドラ、メモリマップドデバイスドライバ、そして独自の低レベルマルチスレッド同期プリミティブの作成です。 しかしながら、この最後の使用のためには、 volatileは十分ではなくそして必要でさえないかもしれない。 同期のためには間違いなくasm(またはコンパイラ固有またはC1xアトミック)も必要です。

volatileは、あなたが尋ねたコードを含め、他の目的には役に立ちません。


グローバル変数は、常にvolatile宣言されるべきではありません。

他のスレッドによって変更される可能性があり、メモリの並べ替えの問題やコンパイラの命令の並べ替えが行われる可能性がある場合にのみ、揮発性である必要があります。 それでも、適切なミューテックスを使用しているのであれば、必要ありません。 しかし一般的には、グローバル変数をミューテックスする必要がある場合は、おそらく設計が不適切です。

編集:それを揮発性にしても、グローバル変数がスレッドセーフであるという意味ではありません!

その他の典型的な用途は、メモリに通常とは異なる方法でアクセスする場合です。たとえば、組み込みのマイクロコンピュータにDMAマップメモリ​​がある場合などです。


いいえ、 volatileキーワードはここでは必要ありません。 global_valueは関数bar外側に表示されるので、コンパイラは別の関数が呼び出されても同じままであるとglobal_valueはいけません。

[更新2011-07-28]私はそれをすべて証明する素晴らしい引用を見つけました。 これはISO C99の5.1.2.3p2にありますが、ここで全体をコピーするのは面倒です。 それは言います:

シーケンスポイントと呼ばれる実行シーケンス内の特定の指定されたポイントで、前の評価のすべての副作用が完全であり、その後の評価の副作用が発生してはならない。

シーケンスポイントは次のとおりです。

  • 引数が評価された後の関数の呼び出し(6.5.2.2)。
  • 完全な式の終わり:[...]式文の中の式(6.8.3); [...]

そこにあなたの証明があります。





qualifiers