4

マルチスレッドプログラムでいくつかのグローバル構造を使用しています。メンバーの中には、複数のスレッドによって同時に変更されるものと、そうでないものがあります。

このメンバーを揮発性として定義しませんでしたが、読み取りと書き込みの両方の目的でこのメンバーを使用するときはいつでも、__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とは定義しません。私は安全ですか?

前もって感謝します。

4

1 に答える 1

4

はい、あなたは安全です。ドキュメントには、揮発性である必要があるとは記載されていないため、そうではありません。

*関数は__sync、必要に応じてメモリバリアとして機能するため、volatile不要になります。__syncそして、とにかく*関数以外のものを使用することはできません(*関数のみがプレフィックス__syncを生成します)。lock

注:__sync*関数はC ++ 11スタイルの__atomic*タイプを優先して、gcc 4.7で非推奨になりましたが、gcc4.6にはまだそれらがありません。

于 2013-02-03T20:10:26.943 に答える