マルチスレッドプログラムでいくつかのグローバル構造を使用しています。メンバーの中には、複数のスレッドによって同時に変更されるものと、そうでないものがあります。
このメンバーを揮発性として定義しませんでしたが、読み取りと書き込みの両方の目的でこのメンバーを使用するときはいつでも、__sync_fetch_and_addなどのアトミックビルトインを使用します。
問題は、このメンバーを定義する必要がありますか、それとも構造体全体を揮発性にする必要がありますか?
このビルトイン(ロックプレフィックス)のため、コンパイラはレジスタではなくメモリにアクセスする必要があると思います。競合状態を引き起こさない他のメンバーについて心配する必要があります。
コンパイラ(gcc 4.6.2)のアセンブリ出力を確認しましたが、私の仮定は正しいようです。
これがテストコードです。
int sum = 0;
for (i=0; i<2000000000; i++) {
sum += i;
}
アセンブリ出力(-O2 -S -masm = intel)
L2:
add edx, eax
inc eax
cmp eax, 2000000000
jne L2
したがって、コンパイラがメモリにアクセスされることはありません(eax = i、edx = sum)
これが2番目のテストコードです。
volatile int sum = 0;
for (i=0; i<2000000000; i++) {
sum += i;
}
アセンブリ出力
L2:
mov edx, DWORD PTR [esp+28]
add edx, eax
mov DWORD PTR [esp+28], edx
inc eax
cmp eax, 2000000000
jne L2
コンパイラは、期待どおりに毎回合計のためにメモリにアクセスしました。
最終的なコード、私のやり方。
int sum = 0;
for (i=0; i<2000000000; i++) {
__sync_fetch_and_add(&sum , i);
}
アセンブリ出力。
L2:
lock add DWORD PTR [esp+28], eax
inc eax
cmp eax, 2000000000
jne L2
以前のように一時レジスタ(edx)でさえなく、コンパイラは毎回メモリにアクセスしました。
したがって、複数のスレッドによって変更されたメンバー、または一度に1つのスレッドのみによって変更されたメンバーをvolatileとは定義しません。私は安全ですか?
前もって感謝します。