[英]std::call_once safe for non atomic variables?
std::call_once
能否正常用於非原子變量? 請考慮以下代碼
std::once_flag once;
int x;
void init() { x = 10; }
void f() {
std::call_once(once, init);
assert(x == 10);
}
int main() {
std::thread t1(f), t2(f);
t1.join();
t2.join();
}
當call_once
返回時,是否會在所有線程中看到init
的副作用? 有關cppreference的文檔有點模糊。 它只在所有線程上說std::call_once
將在init
完成后返回,但是沒有提到任何阻止在init
返回后重新排序x = 10的東西。
有任何想法嗎? 澄清行為的標准在哪里?
當call_once返回時,是否會在所有線程中看到init中的副作用?
來自init
副作用對於調用call_once
所有線程都是可見的。只有一個活動執行(調用init
),但可能有多個被動執行。
§30.4.6.2-2 - [thread.once.callonce]
不調用其func的call_once的執行是被動執行。 調用其func的call_once的執行是一個活動執行。
§30.4.6.2-3 - [thread.once.callonce]
同步:對於任何給定的once_flag:所有活動執行都按總順序發生; 完成一個有效執行與(6.8.2)該總訂單中下一個的開始同步; 並且返回的執行與所有被動執行的返回同步。
所以它完全符合您的預期
原子變量和非原子變量之間的主要區別在於,從多個線程訪問非原子變量(除非所有線程都在讀取)需要顯式同步以防止訪問可能並發。
有多種方法可以實現此同步。 最常見的技術涉及互斥體。 一個線程解鎖互斥鎖與另一個線程隨后鎖定該互斥鎖同步。 因此,如果第一個線程寫入變量而第二個線程讀取該變量,則在寫入和讀取之間存在顯式排序。 然后程序按預期運行:讀取必須看到以該順序寫入的最后一個值。 如果未使用互斥鎖,則對變量的訪問可能是並發的,並且將發生未定義的行為。
原子變量是自同步的:無論如何,試圖訪問相同原子變量的兩個線程將在它們之間產生一些順序。 除此之外,與非原子變量相比,它們沒有任何特殊能力可供多個線程訪問。
多個線程使用帶有相同標志的std::call_once
建立一個顯式同步: 每個線程只在init
完成后才從std::call_once
返回,因此每個線程必須看到x
的新值。
只允許編譯器對寫入進行重新排序,使其不會改變程序的可觀察行為。 一旦遵守標准,您在重新排序方面合理化的競爭條件就會消失,因為不允許寫入非原子變量可能與另一個對同一變量的訪問同時發生。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.