web-dev-qa-db-ja.com

gccアトミック組み込み関数

http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Atomic-Builtins.html

次のコードはvarの値をアトミックに増加すると思います。

volatile int var = 0;
__sync_fetch_and_add( &var, 1 )

上記のコードを次のように理解しましたlogic

  1. 変数varのアドレスをロードします
  2. 変数varに番号1をアトミックに書き込みます-レジスタ/キャッシュを介して、どういうわけか

ただし、以下もアトミックかどうかは疑問です

volatile int var = 0;
volatile int num = 1;
__sync_fetch_and_add( &var, num )

として解釈される可能性があるため

  1. 変数varのアドレスをロードします
  2. 変数numの値をレジスターにロードします
  3. 値を変数varに書き込みます。

#2が実行された後、#3の前に、CPU /スレッドが中断され、別のCPU /スレッドが変数numの値を更新します。

つまり、gccの_sync*()を使用する場合、2番目の引数として定数ではなく変数を使用できますか?

それは原子性を壊しませんか?

23
ddoman

操作は実際には2つの操作です。

__sync_fetch_and_add( &var, num )

numのロードはアトミックです。 varへの追加はアトミックです。ただし、2つのアトミック操作を組み合わせても、アトミック操作は行われません。これが、ロックのない新しいデータ構造を発明することが非常に難しい理由です。 一般的に、2つのスレッドセーフ操作は、合成時に必ずしもスレッドセーフ操作になるとは限りません。これが、正しいマルチスレッドアプリケーションを作成することが非常に難しい理由です。

分かりますか、 __sync_fetch_and_add確かにアトミックですが、通常の関数のように動作します-したがって、「num」の現在の値をパラメーターとして受け取ります。関数の原子性が壊れていると言うのはまったく正しくありません-numから値をロードするのは呼び出し側の責任であり、関数のインターフェイスの一部ではないためです。これについても同様に文句を言うことができます。

__sync_fetch_and_add(&var, some_really_long_function());

さらに悪いことに

__sync_fetch_and_add(long_function_1(), long_function_2());

「と解釈されるかもしれない」とあなたは言う

  1. 変数varのアドレスをロードします
  2. 変数numの値をロードします
  3. アトミック加算を実行します

しかし、C仕様によると、それはmayがこのように解釈されるのではなく、むしろmustがこのように解釈されます。それ以外の場合、コンパイラは適合しません(実際には、#1と#2を入れ替えることができますが、ここでは重要ではありません)。

30
Dietrich Epp