名前空間
変種
操作

アトミック型

提供: cppreference.com
< c‎ | language

目次

[編集] 構文

_Atomic ( type-name ) (1) (C11以上)
_Atomic type-name (2) (C11以上)
1) 型指定子としての使用。 新しいアトミック型を指示します。
2) 型修飾子としての使用。 type-name のアトミックバージョンを指示します。 この使用方法では、 constvolatile、および restrict と混ぜることができます。 他の修飾子と異なり、 type-name のアトミックバージョンは type-name と異なるサイズ、アライメント、オブジェクト表現を持つ可能性があります。
type-name - 配列、関数以外の任意の型。 (1) の場合、 type-name はアトミックであってはならず、 cvr 修飾することもできません。

ヘッダ <stdatomic.h> は、組み込みの型およびライブラリの型と共にこのキーワードを使用することを単純化する、 atomic_bool から atomic_uintmax_t までの37個の便利マクロを定義しています。

_Atomic const int * p1;  // p は atomic const int へのポインタです。
const atomic_int * p2;   // 同上。
const _Atomic(int) * p3; // 同上。

[編集] 説明

アトミック型のオブジェクトはデータ競合を発生しない唯一のオブジェクトです。 つまり、2つのスレッドによって並行的に変更したり、一方から書き込んで他方から読み込んだりしても構いません。

それぞれのアトミックオブジェクトには紐付いた変更順序があります。 これはそのオブジェクトに行われた変更の全順序です。 あるスレッドからの視点において、何らかのアトミックオブジェクト M の変更 A が同じアトミックオブジェクト M の変更 B に対して先行発生する場合、 M の変更順序において A は B の前に発生します。

それぞれのアトミックオブジェクトに紐付いた変更順序はありますが、これは全順序では有りません。 異なるアトミックオブジェクトの変更は異なるスレッドからは異なる順序で観察される可能性があることに注意してください。

すべてのアトミック操作に対して保証される4種類の一貫性があります。

  • 書き込み-書き込みの一貫性。 アトミックオブジェクト M を変更する操作 A が M を変更する操作 B に対して先行発生する場合、 M の変更順序において A は B より前に現れます。
  • 読み込み-読み込みの一貫性。 アトミックオブジェクト M の値計算 A が M の値計算 B に対して先行発生し、 A が M の副作用 X から値を取った場合、 B によって計算される値は X によって格納された値であるか M の変更順序において X より後に現れる M の副作用 Y によって格納された値であるかのいずれかです。
  • 読み込み-書き込みの一貫性。 アトミックオブジェクト M の値計算 A が M の操作 B に対して先行発生する場合、 A は M の変更順序において B より前に現れた M の副作用 X から値を取ります。
  • 書き込み-読み込みの一貫性。 アトミックオブジェクト M の副作用 X が M の値計算 B に対して先行発生する場合、評価 B は X から、または M の変更順序において X より後に現れる副作用 Y から、値を取ります。

一部のアトミック操作は同期操作でもあり、解放のセマンティクス、取得のセマンティクス、または逐次一貫性のセマンティクスを持つことがあります。 memory_order を参照してください。

組み込みのインクリメントおよびデクリメント演算子および複合代入は (memory_order_seq_cst が用いられたかのような) 逐次一貫性の全順序を持つ読み込み-変更-書き込み操作です。 もっと緩い同期のセマンティクスを望む場合は、代わりに標準ライブラリの関数が使用できます。

アトミックの性質は左辺値に対してのみ意味を持ちます。 左辺値から右辺値への変換 (アトミックな場所から CPU レジスタへのメモリ読み込みをモデル化します) は他の修飾子と共にアトミック性を取り除きます。

[編集] ノート

マクロ定数 __STDC_NO_ATOMICS__(C11) がコンパイラによって定義されている場合は、キーワード _Atomic およびヘッダ <stdatomic.h> は提供されません。

アトミックな構造体/共用体のメンバのアクセスは未定義動作です。

ライブラリ型 sig_atomic_t はスレッド間の同期やメモリ順序を提供しません。 アトミック性だけです。

volatile 型はスレッド間の同期、メモリ順序、アトミック性を提供しません。

[編集] キーワード

_Atomic

[編集]

#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
 
atomic_int acnt;
int cnt;
 
int f(void* thr_data)
{
    for(int n = 0; n < 1000; ++n) {
        ++cnt;
        ++acnt;
        // この例では以下のような relaxed メモリ順序でも十分です。
        // atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);
    }
    return 0;
}
 
int main(void)
{
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
 
    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

出力例:

The atomic counter is 10000
The non-atomic counter is 8644

[編集] 参考文献

  • C11 standard (ISO/IEC 9899:2011):
  • 6.7.2.4 Atomic type specifiers (p: 121)
  • 7.17 Atomics <stdatomic.h> (p: 273-286)