所謂原子操作,即一系列複雜的操作能一氣呵成,中間不被其他的操作打斷。這在多線程程式中尤其常見,但要實作這種功能,既要考慮程式的良好設計,又要關心特定平台的體系結構和相關編譯器對原子特性的支援程度。是以,為了簡化這個過程,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對象的目前值,然後為它加上我們傳入的值,最後傳回原來的值。其執行邏輯類似于下面的代碼:
currentValue += valueToAdd;
在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
版權聲明:本文為部落客原創文章,轉載請附上博文連結!