簡體   English   中英

使用 C11 GCC 使數據讀/寫原子化<stdatomic.h> ?

[英]Making data reads/writes atomic in C11 GCC using <stdatomic.h>?

我從這里這里的SO 線程中了解到,假設多線程應用程序中的數據讀/寫在操作系統/硬件級別是原子的是不安全的,並且可能導致數據損壞。 我想知道在 Linux 上使用帶有 GCC 編譯器的<stdatomic.h> C11 庫,使讀取和寫入int變量原子化的最簡單方法。

如果我目前在一個線程中有一個int賦值: messageBox[i] = 2 ,我如何使這個賦值原子化? 同樣用於閱讀測試,例如if (messageBox[i] == 2)

對於 C11 原子,您甚至不必使用函數。 如果您的實現(= 編譯器)支持原子,您只需在變量聲明中添加一個原子說明符,然后對它的所有操作都是原子的:

_Atomic(int) toto = 65;
...
toto += 2;  // is an atomic read-modify-write operation
...
if (toto == 67) // is an atomic read of toto

Atomics 有其價格(它們需要更多的計算資源),但只要您幾乎不使用它們,它們就是同步線程的完美工具。

如果我目前在一個線程中有一個 int 賦值:messageBox[i] = 2,我如何使這個賦值原子化? 同樣用於閱讀測試,例如 if (messageBox[i] == 2)。

你幾乎不需要做任何事情。 在幾乎所有情況下,您的線程共享(或與之通信)的數據都受到保護,不會通過互斥鎖、信號量等事物進行並發訪問。 基本操作的實現保證了內存的同步。

這些原子的原因是幫助您在代碼中構建更安全的競爭條件。 它們有許多危險; 包含:

ai += 7;

如果適當地定義了ai,將使用原子協議。 試圖破譯競爭條件並不能通過模糊實現來幫助。

他們還有一個高度依賴機器的部分。 例如,上面的行在某些平台上可能會失敗[1],但是這種失敗是如何反饋給程序的呢? 不是[2]。

只有一種操作可以選擇處理失敗; atomic_compare_exchange_(弱|強)。 Weak 只嘗試一次,讓程序選擇如何以及是否重試。 強重試無休止。 僅僅嘗試一次是不夠的——由於中斷可能會發生虛假故障——但對非虛假故障進行無休止的重試也不好。

可以說,對於健壯的程序或廣泛適用的庫,您應該使用的唯一位是 atomic_compare_exchange_weak()。

[1] 加載鏈接、條件存儲 (ll-sc) 是在異步總線架構上進行原子事務的常用方法。 加載鏈接在緩存行上設置一個小標志,如果任何其他總線代理嘗試修改該緩存行,該標志將被清除。 如果在緩存中設置了 little 標志,Store-conditional 存儲一個值,並清除該標志; 如果標志被清除,Store-conditional 會發出錯誤信號,因此可以嘗試適當的重試操作。 從這兩個操作中,您可以在完全異步的總線架構上構建您喜歡的任何原子操作。

ll-sc 可能對位置的緩存屬性有微妙的依賴性。 允許的緩存屬性取決於平台,因為可以在 ll 和 sc 之間執行哪些操作。

如果您對緩存不佳的訪問進行 ll-sc 操作,然后盲目重試,您的程序將鎖定。 這不僅僅是猜測; 我不得不在基於 ARMv7 的“安全”系統上調試其中之一。

[2]:

#include <stdatomic.h>
int f(atomic_int *x) {
    return (*x)++;
}
f:
        dmb     ish
.L2:
        ldrex   r3, [r0]
        adds    r2, r3, #1
        strex   r1, r2, [r0]
        cmp     r1, #0
        bne     .L2       /* note the retry loop */
        dmb     ish
        mov     r0, r3
        bx      lr

假設多線程應用程序中的數據讀/寫在操作系統/硬件級別是原子的是不安全的,並且可能導致數據損壞

實際上,像int這樣的類型的非復合操作在所有合理的架構上都是原子的。 你讀到的只是一個騙局。

(增量是一個復合操作:它有一個讀、一個計算和一個寫組件。每個組件都是原子的,但整個復合操作不是。)

但硬件級別的原子性不是問題。 您使用的高級語言根本不支持對常規類型的這種操作。 您需要使用原子類型甚至有權以原子性問題相關的方式操作對象:當您可能修改另一個線程中正在使用的對象時。

(或 volatile 類型。但不要使用 volatile。使用原子。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM