所謂原子操作,即一系列復雜的操作能一氣呵成,中間不被其他的操作打斷。這在多線程程序中尤其常見,但要實現這種功能,既要考慮程序的良好設計,又要關心特定平台的體系結構和相關編譯器對原子特性的支持程度。所以,為了簡化這個過程,Qt為我們提供了QAtomicInteger模板類,該類封裝了大量與原子操作相關的細節和底層特性,為我們提供了方便易用的上層接口。雖然,該類並不能解決所有的原子操作問題,比如在不同的內存模型下,怎么保證對共享變量的原子訪問,還需我們人為的控制,但它已經大大減輕了我們的工作復雜度。
QAtomicInteger模板類主要為我們提供了整數常用的原子操作,如 reference counting、test-and-set、fetch-and-store、fetch-and-add。QAtomicInteger,顧名思義,該類只能應用於整數類型,那么我們下來看下在Qt中哪些整型能使用該類進行原子操作。如下表:
位數 類型
8-bit char, signed char, unsigned char, qint8, quint8
16-bit short, unsigned short, qint16, quint16, char16_t(c++11)
32-bit int, unsigned int, qint32, quint32, char32_t(c++11)
64-bit long long, unsigned long long, qint64, quint64
platform-specific size long, unsigned long
pointer size qintptr, quintptr, qptrdiff
在上表中,只有32-bit 和 pointer-sized 的實例在所有的平台上都能得到保證。但對其他大小的支持需要看特定的編譯器和運行改程序的處理器。為了測試自己的平台是否支持某種類型,可以使用Qt提供的宏Q_ATOMIC_INT{nn}_IS_SUPPORTED,這里的nn就是你想測試的類型的位數。
下面,在具體看QAtomicInteger提供的操作之前,我們先來看一下與原子操作有關的內存順序(內存模型)。
剛才我們說到QAtomicInteger為我們提供了幾種原子操作 test-and-set、fetch-and-store、fetch-and-add。其實這些函數的實現都定義了一種內存順序的語義,這個語義描述了當處理器執行原子語句時怎么訪問這些原子語句及其 前后的內存。因為當代的處理器架構允許對內存進行隨意的訪問,所以,為了讓程序在所以的處理器上都能正確執行,使用一種合適的內存訪問語義是至關重要的。在Qt中,為我們提供了4中內存模型:
Relaxed - 即不具體指定內存訪問的順序,編譯器和處理器可以自由的對內存訪問進行重新排序。
Acquire - 原子操作之后的內存訪問(已程序的順序)不會在原子操作之前被重新排序。
Release - 原子操作之前的內存訪問(已程序的順序)不會在原子操作之后被重新排序。
Ordered - Acquire 和 Release 的組合。
接下來,我們具體看下相關的原子操作API:
Reference counting
函數ref() 和 deref() 提供了高效的引用計數API。這些函數的返回值表明了什么時候最后一個引用被是釋放了。這些功能可以用來實現我們自己的隱式共享類。如下代碼所示:
MySharedType &MySharedType::operator=(const MySharedType &other)
{
(void) other.data->atomicInt.ref();
if (!data->atomicInt.deref()) {
// The last reference has been released
delete d;
}
d = other.d;
return *this;
}
Test-and-set
這些函數完成的功能是如果QAtomicInteger的當前值等於我們傳入的期望值,則test-and-set函數會為其賦一個新值,然后返回true。如果當前值不等於傳入的期望值,則這些函數聲明也不干,直接返回false。即等價於一下的代碼邏輯:
if (currentValue == expectedValue) {
currentValue = newValue;
return true;
}
return false;
在QAtomicInteger中為我們提供了4個test-and-set函數,分別是:testAndSetRelaxed()、testAndSetAcquire()、testAndSetRelease()、testAndSetOrdered()。其實就是以不同的內存模型進行操作。
Fetch-and-store
fetch-and-store 函數的功能是讀取QAtomicInteger對象的當前值,並且為它設置一個我們傳入的新值,然后返回讀取到的舊值。該操作等同與以下的代碼邏輯:
int originalValue = currentValue;
currentValue = newValue;
return originalValue;
在QAtomicInteger中有4個fetch-and-store函數:fetchAndStoreRelaxed()、fetchAndStoreAcquire()、fetchAndStoreRelease()、fetchAndStoreOrdered()。
Fetch-and-add
fetch-and-add函數讀取QAtomicInteger對象的當前值,然后為它加上我們傳入的值,最后返回原來的值。其執行邏輯類似於下面的代碼:
int originalValue = currentValue;
currentValue += valueToAdd;
return originalValue;
在QAtomicInteger中有4個fetch-and-add方法: fetchAndAddRelaxed()、fetchAndAddAcquire()、fetchAndAddRelease()、fetchAndAddOrdered()。
特性測試相關的API
提供一個平台無關的、能應用於所以處理器的原子操作API是有挑戰性的。所以,QAtomicInteger類提供的API能保證在所有的處理器的完成原子操作,但是,並不是所以的處理器都支持QAtomicInteger所提供的這些操作。所以,在使用這些操作之前,檢測一下當前處理器是否支持某個API是很重要的。
所以Qt提供了大量的宏,你可以使用這些宏在編譯器就可以檢測你的硬件是否支持某個特性。這些宏會告訴你你的硬件是 支持該操作、有時支持該操作、不支持該操作。並且,這些宏有大致相同的形式,方便記憶,類似於 Q_ATOMIC_INTnn_OPERATION_IS_HOW_NATIVE。其中,nn是你要測試的整形的位數,operation是REFERENCE_COUNTING、TEST_AND_SET、FETCH_AND_STORE、FETCH_AND_ADD 其中之一,how是ALWAYS、SOMETIMES、NOT其中之一。並且,對應每種組合只有一個確定的宏。例如,如果Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_ALWAYS_NATIVE 被定義了,那么Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE 和 Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_NOT_NATIVE 都不會被定義。
如果一個操作能在常量時間內完成,我們說它是wait-free。這類操作的實現不需要用到鎖或者某種循環。並且,在Qt中,被平台一直支持的原子操作都是wait-free的。另外,Qt還定義了宏Q_ATOMIC_INTnn_OPERATION_IS_WAIT_FREE 來檢測一個原子操作是否是wait-free的。
除此之外,有些原子操作只能在較新的處理器上唄支持,所以,我們除了需要在編譯時檢測某個特性是否被支持外,在程序運行時也需要這種檢測。所以,Qt除了提供上面的宏用於編譯時檢測,也提供了幾個API用戶在代碼中進行運行時的檢測,如 isReferenceCountingNative()、isTestAndSetNative()、isFetchAndStoreNative()、isFetchAndAddNative()。同時,也提供了對wait-free特性的檢測函數,如isReferenceCountingWaitFree()、isTestAndSetWaitFree()、isFetchAndStoreWaitFree()、isFetchAndAddWaitFree()。
Qt中的原子操作在不同的版本中是不同的,所以Qt處於對老版本的兼容性,規定不帶nn的宏就等價於32-bit的宏。例如,Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE 等價於 Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_WAIT_FREE。
最后,上面只是從大的方面講解了QAtomicInteger類的功能,至於其中的每個函數的具體使用也都是見名知意的,比如 ++、--、load、store等,在此就不一一講解了,大家可以在用到時,參考Qt幫助文檔即可。另外,Qt還提供了QAtomicInt 和 QAtomicPointer 類,用法與此類似。
---------------------
作者:求道玉
來源:CSDN
原文:https://blog.csdn.net/Amnes1a/article/details/62881888
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
