atomic | #include 原子操作(atomic): 互斥量的加鎖一般是針對(duì)一個(gè)代碼段,而原子操作針對(duì)的一般都是一個(gè)變量。原子變量既不可復(fù)制亦不可移動(dòng)。(1)它表示在多個(gè)線程訪問同一個(gè)全局資源的時(shí)候,能夠確保所有其他的線程都不在同一時(shí)間內(nèi)訪問相同的資源。也就是他確保了在同一時(shí)刻只有唯一的線程對(duì)這個(gè)資源進(jìn)行訪問。這有點(diǎn)類似互斥對(duì)象對(duì)共享資源的訪問的保護(hù),但是原子操作更加接近底層,因而效率更高。是線程安全的。 (2)原子數(shù)據(jù)類型不會(huì)發(fā)生數(shù)據(jù)競(jìng)爭(zhēng),能直接用在多線程中而不必我們用戶對(duì)其進(jìn)行添加互斥資源鎖的類型。從實(shí)現(xiàn)上,大家可以理解為這些原子類型內(nèi)部自己加了鎖。 (3)C++11中所有的原子類都是不允許拷貝、不允許Move的,atomic_flag也不例外。 (4)C++11 對(duì)常見的原子操作進(jìn)行了抽象,定義出統(tǒng)一的接口,并根據(jù)編譯選項(xiàng)/環(huán)境產(chǎn)生平臺(tái)相關(guān)的實(shí)現(xiàn)。新標(biāo)準(zhǔn)將原子操作定義為atomic模板類的成員函數(shù),囊括了絕大多數(shù)典型的操作——讀、寫、比較、交換等 contained type | atomic type | bool | atomic_bool | char | atomic_char | signed char | atomic_schar | unsigned char | atomic_uchar | short | atomic_short | unsigned short | atomic_ushort | int | atomic_int | unsigned int | atomic_uint | long | atomic_long | unsigned long | atomic_ulong | long long | atomic_llong | unsigned long long | atomic_ullong | wchar_t | atomic_wchar_t | char16_t | atomic_char16_t | char32_t | atomic_char32_t | intmax_t | atomic_intmax_t | uintmax_t | atomic_uintmax_t | int_leastN_t | atomic_int_leastN_t | uint_leastN_t | atomic_uint_leastN_t | int_fastN_t | atomic_int_fastN_t | uint_fastN_t | atomic_uint_fastN_t | intptr_t | atomic_intptr_t | uintptr_t | atomic_uintptr_t | size_t | atomic_size_t | ptrdiff_t | atomic_ptrdiff_t |
(4)macro macro | relative to types | ATOMIC_BOOL_LOCK_FREE | bool | ATOMIC_CHAR_LOCK_FREE | char signed char unsigned char | ATOMIC_SHORT_LOCK_FREE | short unsigned short | ATOMIC_INT_LOCK_FREE | int unsigned int | ATOMIC_LONG_LOCK_FREE | long unsigned long | ATOMIC_LLONG_LOCK_FREE | long long unsigned long long | ATOMIC_WCHAR_T_LOCK_FREE | wchar_t | ATOMIC_CHAR16_T_LOCK_FREE | char16_t | ATOMIC_CHAR32_T_LOCK_FREE | char32_t | ATOMIC_POINTER_LOCK_FREE | U* (for any type U) |
(5)memory_order:內(nèi)存順序 序號(hào) | 值 | 意義 | 1 | memory_order_relaxed | 寬松模型,不對(duì)執(zhí)行順序做保證 | 2 | memory_order_consume | 當(dāng)前線程中,滿足happens-before原則。 當(dāng)前線程中該原子的所有后續(xù)操作,必須在本條操作完成之后執(zhí)行 | 3 | memory_order_acquire | 當(dāng)前線程中,讀操作滿足happens-before原則。 所有后續(xù)的讀操作必須在本操作完成后執(zhí)行 | 4 | memory_order_release | 當(dāng)前線程中,寫操作滿足happens-before原則。 所有后續(xù)的寫操作必須在本操作完成后執(zhí)行 | 5 | memory_order_acq_rel | 當(dāng)前線程中,同時(shí)滿足memory_order_acquire和memory_order_release | 6 | memory_order_seq_cst | 最強(qiáng)約束。全部讀寫都按順序執(zhí)行 |
(6)Functions (7)std::atomic的限制:trivially copyable(可平凡復(fù)制):一個(gè)類型如果是trivially copyable,則使用memcpy這種方式把它的數(shù)據(jù)從一個(gè)地方拷貝出來會(huì)得到相同的結(jié)果。 1.沒有non-trivial 的拷貝構(gòu)造函數(shù) 2.沒有non-trivial的move構(gòu)造函數(shù) 3.沒有non-trivial的賦值操作符 4.沒有non-trivial的move賦值操作符 5.有一個(gè)trivial的析構(gòu)函數(shù) | std::atomic_flag:最簡(jiǎn)單的原子變量實(shí)例,是對(duì)于bool類型的變量進(jìn)行原子操作,提供了標(biāo)志的管理,標(biāo)志有三種狀態(tài):clear、set和未初始化狀態(tài)。 接口介紹: (1)ATOMIC_FLAG_INIT:用于給atomic_flag變量賦初值,如果定義后為賦值,則狀態(tài)是不確定的。被這個(gè)賦值后的狀態(tài)為false。 (2)test_and_set() :接口函數(shù),調(diào)用后狀態(tài)變?yōu)閠rue,并返回改變狀態(tài)前的狀態(tài)值。 (3)clear() : 接口函數(shù),調(diào)用后狀態(tài)變?yōu)閒alse。 | #include #include #include #include std::atomic_flag lock = ATOMIC_FLAG_INIT; int gcnt = 0; void f(int n) { for (int cnt = 0; cnt < 100; ++cnt) { while (lock.test_and_set(std::memory_order_acquire)) // 獲得鎖 ; // 自旋 std::cout << "Output from thread " << n << ''; gcnt++; lock.clear(std::memory_order_release); // 釋放鎖 } } int main() { std::vector v; for (int n = 0; n < 10; ++n) { v.emplace_back(f, n); } for (auto& t : v) { t.join(); } } 自旋鎖的解釋:當(dāng)某一個(gè)線程調(diào)用‘lock.test_and_set’時(shí),即設(shè)置lock的狀態(tài)為true,當(dāng)另一個(gè)線程進(jìn)入時(shí),再次調(diào)用test_and_set時(shí)返回的狀態(tài)為true,則一直在while循環(huán)中不斷獲取,即實(shí)現(xiàn)了等待,直到第一個(gè)線程調(diào)用clear將狀態(tài)變?yōu)閒alse。 | std::atomic:通過atomic模板類可以對(duì)更多的類型進(jìn)行原子操作 (1)is_lock_free:通過這個(gè)接口判斷是否需要加鎖。如果是,則在多個(gè)線程訪問該對(duì)象時(shí)不會(huì)導(dǎo)致線程阻塞(可能使用某種事務(wù)內(nèi)存transactional memory方法實(shí)現(xiàn)lock-free的特性)。事實(shí)上該函數(shù)可以做為一個(gè)靜態(tài)函數(shù)。所有指定相同類型T的atomic實(shí)例的is_lock_free函數(shù)都會(huì)返回相同值。 (2)store:賦值操作。operator=實(shí)際上內(nèi)部調(diào)用了store,并返回d。 void store(T desr, memory_order m = memory_order_seq_cst) noexcept;void store(T desr, memory_order m = memory_order_seq_cst) volatile noexcept;T operator=(T d) noexcept;T operator=(T d) volatile noexcept; (3)load: 讀取,加載并返回變量的值。operator T是load的簡(jiǎn)化版,內(nèi)部調(diào)用的是load(memory_order_seq_cst)形式。 (4)exchange:交換,賦值后返回變量賦值前的值。exchange也稱為read-modify-write操作。 T exchange(T desr, memory_order m = memory_order_seq_cst) volatile noexcept; T exchange(T desr, memory_order m = memory_order_seq_cst) noexcept; (5)compare_exchange_weak:這就是有名的CAS(Compare And Swap: 比較并交換)。操作是原子的,排它的。其它線程如果想要讀取或修改該原子對(duì)象時(shí),會(huì)等待先該操作完成。 (6)compare_exchange_strong: 進(jìn)行compare時(shí),與weak版一樣,都是比較的物理內(nèi)容。與weak版不同的是,strong版本不會(huì)返回偽false。即:原子對(duì)象所封裝的值如果與expect在物理內(nèi)容上相同,strong版本一定會(huì)返回true。其所付出的代價(jià)是:在某些需要循環(huán)檢測(cè)的算法,或某些平臺(tái)下,其性能較compare_exchange_weak要差。但對(duì)于某些不需要采用循環(huán)檢測(cè)的算法而言, 通常采用compare_exchange_strong 更好。 | | std::atomic特化: (1)fetch_add:該函數(shù)將原子對(duì)象封裝的值加上v,同時(shí)返回原子對(duì)象的舊值。其功能用偽代碼表示為: // T is integral T fetch_add(T v, memory_order m = memory_order_seq_cst) volatile noexcept; T fetch_add(T v, memory_order m = memory_order_seq_cst) noexcept; // T is pointer T fetch_add(ptrdiff_t v, memory_order m = memory_order_seq_cst) volatile noexcept; T fetch_add(ptrdiff_t v, memory_order m = memory_order_seq_cst) noexcept; (2)fetch_sub:該函數(shù)將原子對(duì)象封裝的值減去v,同時(shí)返回原子對(duì)象的舊值。其功能用偽代碼表示為: // T is integral T fetch_sub(T v, memory_order m = memory_order_seq_cst) volatile noexcept; T fetch_sub(T v, memory_order m = memory_order_seq_cst) noexcept; // T is pointer T fetch_sub(ptrdiff_t v, memory_order m = memory_order_seq_cst) volatile noexcept; T fetch_sub(ptrdiff_t v, memory_order m = memory_order_seq_cst) noexcept; (3)++, –, +=, -=:不管是基于整數(shù)的特化,還是指針特化,atomic均支持這四種操作。其用法與未封裝時(shí)一樣 | | 獨(dú)屬于數(shù)值型特化的原子操作 – 位操作: (1)fetch_and,fetch_or,fetch_xor: 位操作,將contained按指定方式進(jìn)行位操作,并返回contained的舊值。 integral fetch_and(integral v, memory_order m = memory_order_seq_cst) volatile noexcept; integral fetch_and(integral v, memory_order m = memory_order_seq_cst) noexcept; integral fetch_or(integral v, memory_order m = memory_order_seq_cst) volatile noexcept; integral fetch_or(integral v, memory_order m = memory_order_seq_cst) noexcept; integral fetch_xor(integral v, memory_order m = memory_order_seq_cst) volatile noexcept; integral fetch_xor(integral v, memory_order m = memory_order_seq_cst) noexcept; (2)perator &=,operator |=,operator ^=:與相應(yīng)的fetch_*操作不同的是,operator操作返回的是新值 T operator &=(T v) volatile noexcept {return fetch_and(v) & v;} T operator &=(T v) noexcept {return fetch_and(v) & v;} T operator |=(T v) volatile noexcept {return fetch_or(v) | v;} T operator |=(T v) noexcept {return fetch_or(v) | v;} T operator ^=(T v) volatile noexcept {return fetch_xor(v) ^ v;} T operator ^=(T v) noexcept {return fetch_xor(v) ^ v;} | |
|