名稱空間
變體
操作

原子型別

來自 cppreference.com
< c‎ | language

目錄

[編輯] 語法

_Atomic ( type-name ) (1) (C11 起)
_Atomic type-name (2) (C11 起)
1) 用作型別說明符;這指定一個新的原子型別
2) 用作型別限定符;這指定 type-name 的原子版本。在此作用下,它可以與 constvolatilerestrict 混合使用,但與其他限定符不同,type-name 的原子版本可能具有不同的尺寸、對齊方式和物件表示。
型別名稱 - 除陣列或函式以外的任何型別。對於 (1)type-name 也不能是原子型別或 cvr 限定的

標頭檔案 <stdatomic.h> 定義了許多便利的類型別名,從 atomic_boolatomic_uintmax_t,它們簡化了此關鍵字與內建型別和庫型別的使用。

_Atomic const int* p1;  // p is a pointer to an atomic const int
const atomic_int* p2;   // same
const _Atomic(int)* p3; // same

如果編譯器定義了宏常量 __STDC_NO_ATOMICS__,則不提供關鍵字 _Atomic

[編輯] 解釋

原子型別物件是唯一不受資料競爭影響的物件;也就是說,它們可以由兩個執行緒併發修改,或者由一個執行緒修改並由另一個執行緒讀取。

每個原子物件都有其自己的相關修改順序,這是對該物件所做的修改的完全排序。如果從某個執行緒的角度來看,對某個原子 M 的修改 A 先於對同一原子 M 的修改 B 發生,則在 M 的修改順序中,A 出現在 B 之前。

請注意,儘管每個原子物件都有其自己的修改順序,但沒有單一的完全順序;不同的執行緒可能以不同的順序觀察對不同原子物件的修改。

所有原子操作都保證有四種一致性:

  1. 寫-寫一致性:如果修改原子物件 M 的操作 A 先於修改 M 的操作 B 發生,則 AM 的修改順序中出現在 B 之前。
  2. 讀-讀一致性:如果原子物件 M 的值計算 A 先於 M 的值計算 B 發生,並且 AM 上的副作用 X 獲取其值,則 B 計算出的值要麼是 X 儲存的值,要麼是 M 上的副作用 Y 儲存的值,其中 YM 的修改順序中出現在 X 之後。
  3. 讀-寫一致性:如果原子物件 M 的值計算 A 先於M 的操作 B 發生,則 AM 上的副作用 X 獲取其值,其中 XM 的修改順序中出現在 B 之前。
  4. 寫-讀一致性:如果原子物件 M 上的副作用 X 先於 M 的值計算 B 發生,則評估 BX 或從在 M 的修改順序中出現在 X 之後的副作用 Y 獲取其值。

一些原子操作也是同步操作;它們可能具有額外的釋放語義、獲取語義或順序一致語義。參見 memory_order

內建的增量和減量運算子以及複合賦值是具有完全順序一致排序(如同使用 memory_order_seq_cst)的讀-改-寫原子操作。如果需要較不嚴格的同步語義,則可以使用標準庫函式

原子屬性僅對左值表示式有意義。左值到右值轉換(它模擬從原子位置到 CPU 暫存器的記憶體讀取)會剝離原子性以及其他限定符。

[編輯] 注意

訪問原子結構體/聯合體的成員是未定義行為。

庫型別 sig_atomic_t 不提供執行緒間同步或記憶體排序,只提供原子性。

volatile 型別不提供執行緒間同步、記憶體排序或原子性。

建議實現確保 C 中 _Atomic(T) 的表示與 C++ 中 std::atomic<T> 的表示對於每種可能的型別 T 都相同。用於確保原子性和記憶體排序的機制應該相容。

[編輯] 關鍵字

_Atomic

[編輯] 示例

#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
 
atomic_int acnt;
int cnt;
 
int f(void* thr_data)
{
    for (int n = 0; n < 1000; ++n)
    {
        ++cnt;
        ++acnt;
        // for this example, relaxed memory order is sufficient, e.g.
        // 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

[編輯] 參考文獻

  • C23 標準 (ISO/IEC 9899:2024)
  • 6.7.2.4 原子型別說明符 (p: TBD)
  • 7.17 原子操作 <stdatomic.h> (p: TBD)
  • C17 標準 (ISO/IEC 9899:2018)
  • 6.7.2.4 原子型別說明符 (p: 87)
  • 7.17 原子 <stdatomic.h> (p: 200-209)
  • C11 標準 (ISO/IEC 9899:2011)
  • 6.7.2.4 原子型別說明符 (p: 121)
  • 7.17 原子操作 <stdatomic.h> (p: 273-286)

[編輯] 另請參見

併發支援庫
C++ 文件 針對 atomic