首先,volatile
并不意味著原子訪問。它是為諸如內存映射I/O和信號處理之類的東西而設計的。volatile
在一起使用時完全沒有必要std::atomic
,除非你的平臺文件另有規定,volatile
對線程之間的原子訪問或內存排序沒有影響。
如果有一個在線程之間共享的全局變量,如:
std::atomic<int> ai;
然后,可見性和排序約束取決于用于操作的內存排序參數,以及鎖、線程和對其他原子變量的訪問的同步效果。
在沒有任何附加同步的情況下,如果一個線程將一個值寫入ai
然后,沒有任何東西可以保證另一個線程在任何給定的時間段內都會看到該值。該標準規定“在合理的時間內”它應該是可見的,但是任何給定的訪問都可能返回一個陳舊的值。
的默認內存順序。std::memory_order_seq_cst
為所有用戶提供一個單一的全局總訂單。std::memory_order_seq_cst
所有變量的操作。這并不意味著您不能得到陳舊的值,但它確實意味著您得到的值決定并由您的操作在這個總順序中的位置決定。
如果有兩個共享變量x
和y
,最初為零,并有一個線程將1寫到x
另一個寫2到y
,讀取這兩個操作的第三個線程可能會看到(0,0)、(1,0)、(0,2)或(1,2),因為操作之間沒有排序約束,因此操作可能以全局順序出現在任何順序。
如果兩個寫入都來自同一個線程,則x=1
以前y=2
讀取線程讀取y
以前x
則(0,2)不再是有效的選項,因為y==2
意味著先前寫到x
是可見的。其他3對(0,0)、(1,0)和(1,2)仍然是可能的,這取決于2對與2寫的交織方式。
如果使用其他內存順序,如std::memory_order_relaxed
或std::memory_order_acquire
這樣,約束就會進一步放松,單一的全局排序就不再適用了。如果沒有額外的同步,線程甚至不必就兩個存儲的順序來分離變量達成一致。
確保您擁有“最新”值的唯一方法是使用讀-修改-寫入操作,例如exchange()
, compare_exchange_strong()
或fetch_add()
..讀-修改-寫入操作有一個附加約束,它們總是對“最新”值進行操作,因此ai.fetch_add(1)
由一系列線程進行的操作將返回一個沒有重復或空白的值序列。在沒有附加約束的情況下,仍然無法保證哪些線程會看到哪些值。
使用原子操作是一個復雜的主題。我建議您閱讀大量背景材料,并在使用Atomics編寫產品代碼之前檢查已發布的代碼。在大多數情況下,編寫使用鎖的代碼更容易,而且效率也不會明顯降低。