国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

《C++并發(fā)編程實(shí)戰(zhàn)》讀書筆記(4):原子變量

這篇具有很好參考價值的文章主要介紹了《C++并發(fā)編程實(shí)戰(zhàn)》讀書筆記(4):原子變量。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1、標(biāo)準(zhǔn)原子類型

標(biāo)準(zhǔn)原子類型的定義位于頭文件<atomic>內(nèi)。原子操作的關(guān)鍵用途是取代需要互斥的同步方式,但假設(shè)原子操作本身也在內(nèi)部使用了互斥,就很可能無法達(dá)到期望的性能提升。有三種方法來判斷一個原子類型是否屬于無鎖數(shù)據(jù)結(jié)構(gòu):

  • 所有標(biāo)準(zhǔn)原子類型(std::atomic_flag除外,因?yàn)樗仨毑扇o鎖操作)都具有成員函數(shù)is_lock_free(),若它返回true則表示給定類型上的操作是能由原子指令直接實(shí)現(xiàn)的,若返回false則表示需要借助編譯器和程序庫的內(nèi)部鎖來實(shí)現(xiàn)。
  • C++程序庫提供了一組宏:ATOMIC_BOOL_LOCK_FREEATOMIC_CHAR_LOCK_FREE、ATOMIC_CHAR16_T_LOCK_FREE、ATOMIC_CHAR32_T_LOCK_FREE、ATOMIC_WCHAR_T_LOCK_FREEATOMIC_SHORT_LOCK_FREE、ATOMIC_INT_LOCK_FREE、ATOMIC_LONG_LOCK_FREEATOMIC_LLONG_LOCK_FREE、ATOMIC_POINTER_LOCK_FREE。宏取值為0表示對應(yīng)的std::atomic<>特化類型從來都不屬于無鎖結(jié)構(gòu),取值為1表示運(yùn)行時才能確定是否屬于無鎖結(jié)構(gòu),取值為2表示它一直屬于無鎖結(jié)構(gòu)。
  • 從C++17開始,全部原子類型都含有一個靜態(tài)常量表達(dá)式成員變量X::is_always_lock_free,功能與上述那些宏相同,用于在編譯期判定一個原子類型是否屬于無鎖結(jié)構(gòu)。當(dāng)且僅當(dāng)在所有支持運(yùn)行該程序的硬件上,原子類型X全都以無鎖結(jié)構(gòu)形式實(shí)現(xiàn),該成員變量的值才為true。

除了std::atomic_flag,其余原子類型都是通過模板std::atomic<>特化得到的。由內(nèi)建類型特化得到的原子類型,其接口反映出自身性質(zhì),例如C++標(biāo)準(zhǔn)沒有為普通指針定義位運(yùn)算(如&=),所以不存在專為原子化指針而定義的位運(yùn)算。一些內(nèi)建類型的std::atomic<>特化如下表:

原子類型的別名 對應(yīng)的特化
atomic_bool std::atomic<bool>
atomic_char std::atomic<char>
atomic_schar std::atomic<signed char>
atomic_uchar std::atomic<unsigned char>
atomic_int std::atomic<int>
atomic_uint std::atomic<unsigned>
atomic_short std::atomic<short>
atomic_ushort std::atomic<unsigned short>
atomic_long std::atomic<long>
atomic_ulong std::atomic<unsigned long>
atomic_llong std::atomic<long long>
atomic_ullong std::atomic<unsigned long long>
atomic_char16_t std::atomic<char16_t>
atomic_char32_t std::atomic<char32_t>
atomic_wchar_t std::atomic<wchar_t>

原子類型對象無法復(fù)制,也無法賦值,但可以接受內(nèi)建類型賦值,也支持隱式地轉(zhuǎn)換成內(nèi)建類型。需要注意的是:按照C++慣例,賦值操作符通常返回一個引用,指向接受賦值的目標(biāo)對象;而原子類型的賦值操作符不返回引用,而是按值返回(該值屬于對應(yīng)的非原子類型)。

2、原子操作

各種原子類型上可以執(zhí)行的操作如下表所示:

操作 atomic_flag atomic<bool> atomic<T*> 整數(shù)原子類型 其它原子類型
test_and_set Y
clear Y
is_lock_free Y Y Y Y
load Y Y Y Y
store Y Y Y Y
exchange Y Y Y Y
compare_exchange_weak, compare_exchange_strong Y Y Y Y
fetch_add, += Y Y
fetch_sub, -= Y Y
fetch_or, |= Y
fetch_and, &= Y
fetch_xor, ^= Y
++, -- Y Y

2.1、操作std::atomic_flag

std::atomic_flag是最簡單的標(biāo)準(zhǔn)原子類型,表示一個布爾標(biāo)志,它只有兩種狀態(tài):成立或置零。std::atomic_flag對象必須由宏ATOMIC_FLAG_INIT初始化,它把標(biāo)志初始化為置零狀態(tài),例如:std::atomic_flag f = ATOMIC_FLAG_INIT;。如果不進(jìn)行初始化,則std::atomic_flag對象的狀態(tài)是未指定的。std::atomic_flag有兩個成員函數(shù):

  • clear():將標(biāo)志清零。
  • test_and_set():獲取舊值并設(shè)置標(biāo)志成立。

使用std::atomic_flag實(shí)現(xiàn)一個自旋鎖的示例如下:

class spinlock_mutex
{
    std::atomic_flag flag;
public:
    spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}
    void lock()
    {
        while (flag.test_and_set());
    }
    void unlock()
    {
        flag.clear();
    }
};

2.2、操作std::atomic<bool>

相比于std::atomic_flagstd::atomic<bool>是一個功能更齊全的布爾標(biāo)志。盡管它也無法拷貝構(gòu)造或拷貝賦值,但還是能依據(jù)非原子布爾量創(chuàng)建其對象,也能接受非原子布爾量的賦值:

std::atomic<bool> b(true);
b = false;

store()是存儲操作,可以向原子對象寫入值。load()是載入操作,可以讀取原子對象的值。exchange()是“讀-改-寫”操作,它獲取原有的值,然后用自行選定的新值作為替換。

std::atomic<bool> b;
bool x = b.load();
b.store(true);
x = b.exchange(false);

compare_exchange_weak()compare_exchange_strong()被稱為“比較-交換”操作,它們的作用是:使用者給定一個期望值,原子變量將它和自身的值進(jìn)行比較,如果相等,就存入另一既定的值;否則,更新期望值所屬的變量,向它賦予原子變量的值。“比較-交換”操作返回布爾類型,如果完成了保存動作(前提是兩值相等),則返回true,否則返回false。對于compare_exchange_weak(),即使原子變量的值等于期望值,保存動作還是有可能失敗,在這種情形下,原子變量維持原值不變,函數(shù)返回false。原子化的“比較-交換”必須由一條指令單獨(dú)完成,而某些處理器沒有這種指令,無從保證該操作按原子化方式完成。要實(shí)現(xiàn)“比較-交換”,負(fù)責(zé)的線程則須改為連續(xù)運(yùn)行一系列指令,但在這些計(jì)算機(jī)上,只要出現(xiàn)線程數(shù)量多于處理器數(shù)量的情形,線程就有可能執(zhí)行到中途因系統(tǒng)調(diào)度而切出,導(dǎo)致操作失敗。這種敗因不是變量值本身存在問題,而是函數(shù)執(zhí)行時機(jī)不對,所以compare_exchange_weak()往往必須配合循環(huán)使用。

bool expected = false;
extern atomic<bool> b;
while(!b.compare_exchange_weak(expected,true) && !expected);

2.3、操作std::atomic<T*>

除了std::atomic<bool>所支持的操作外,std::atomic<T*>還支持算術(shù)形式的指針運(yùn)算。fetch_add()fetch_sub()分別就對象中存儲的地址進(jìn)行原子化加減,然后返回原來的地址。另外,該原子類型還具有包裝成重載運(yùn)算符的+=-=,以及++--的前后綴版本,這些運(yùn)算符作用在原子類型之上,效果與作用在內(nèi)建類型上一樣。

class Foo {};
Foo some_array[5];
std::atomic<Foo*> p(some_array);
Foo* x = p.fetch_add(2);
assert(x == some_array);
assert(p.load() == &some_array[2]);
x = (p -= 1);
assert(x == &some_array[1]);
assert(p.load() == &some_array[1]);

2.4、操作標(biāo)準(zhǔn)整數(shù)原子類型

std::atomic<int>這樣的整數(shù)原子類型上,除了std::atomic<T*>所支持的操作外,還支持fetch_and()、fetch_or()fetch_xor()操作,也支持對應(yīng)的&=、|=、^=復(fù)合賦值形式。

2.5、泛化的std::atomic<>類模板

除了前文的標(biāo)準(zhǔn)原子類型,使用者還能利用泛化模板,依據(jù)自定義類型創(chuàng)建其它原子類型。然而,對于某個自定義的類型UDT,必須要滿足一定條件才能具現(xiàn)化出std::atomic<UDT>

  • 必須具有平實(shí)拷貝賦值運(yùn)算符(平直、簡單的原始內(nèi)存賦值及其等效操作)。若自定義類型具有基類或非靜態(tài)數(shù)據(jù)成員,則它們同樣必須具備平實(shí)拷貝賦值運(yùn)算符。
  • 不得含有虛函數(shù),也不可以從虛基類派生得出。
  • 必須由編譯器代其隱式生成拷貝賦值運(yùn)算符。

由于以上限制,賦值操作不涉及任何用戶編寫的代碼,因此編譯器可以借用memcpy()或采取與之等效的行為完成它。另外值得注意的是,“比較-交換”操作采取的是逐位比較運(yùn)算,效果等同于直接使用memcmp()函數(shù)。

3、內(nèi)存順序

編譯器優(yōu)化代碼時可能會進(jìn)行指令重排,而且CPU執(zhí)行指令時也可能會亂序執(zhí)行,所以代碼的執(zhí)行順序不一定和書寫順序一致。例如下面的代碼可能會按照如表所示的順序執(zhí)行,從而引發(fā)斷言錯誤??梢钥闯?,指令重排在單線程環(huán)境下不會造成邏輯錯誤,但在多線程環(huán)境下可能會造成邏輯錯誤。

int a = 0;
bool flag = false;
void func1()
{
    a = 1;
    flag = true;
}
void func2()
{
    if (flag)
    {
        assert(a == 1);
    }
}
std::thread t1(func1);
std::thread t2(func2);
t1.join();
t2.join();
step 線程t1 線程t2
1 flag = true
2 if (flag)
3 assert(a == 1)
4 a = 1

內(nèi)存順序的作用,本質(zhì)上是要限制單個線程中的指令順序,從而解決多線程環(huán)境下可能出現(xiàn)的問題。原子類型上的操作服從6種內(nèi)存順序,在不同的CPU架構(gòu)上,這幾種內(nèi)存模型也許會有不同的運(yùn)行開銷。

enum memory_order {
    memory_order_relaxed,
    memory_order_consume,
    memory_order_acquire,
    memory_order_release,
    memory_order_acq_rel,
    memory_order_seq_cst
};
  • memory_order_seq_cst

    這是所有原子操作的內(nèi)存順序參數(shù)的默認(rèn)值,語義上要求底層提供順序一致性模型,不存在任何重排,可以解決一切問題,但是效率最低。

  • memory_order_release / memory_order_acquire / memory_order_consume

    release操作可以阻止這個調(diào)用之前的讀寫操作被重排到后面去;acquire操作則可以保證這個調(diào)用之后的讀寫操作不會重排到前面去;consume操作比acquire操作寬松一些,它只保證這個調(diào)用之后的對原子變量有依賴的操作不會被重排到前面去。release與acquire/consume操作需要在同一個原子對象上配對使用,例如:

    std::atomic<int> a;
    std::atomic<bool> flag;
    void func1()
    {
        a = 1;
        flag.store(true, memory_order_release);
    }
    void func2()
    {
        if (flag.load(memory_order_acquire))
        {
            assert(a == 1);
        }
    }
    
  • memory_order_acq_rel

    兼具acquire和release的特性。

  • memory_order_relaxed

    只保證原子類型的成員函數(shù)操作本身是不可分割的,但是對于順序性不做任何保證。

三類操作支持的內(nèi)存順序如下表所示:文章來源地址http://www.zghlxwxcb.cn/news/detail-695019.html

存儲(store)操作 載入(load)操作 “讀-改-寫”(read-modify-write)操作
memory_order_seq_cst Y Y Y
memory_order_release Y Y
memory_order_acquire Y Y
memory_order_consume Y Y
memory_order_acq_rel Y
memory_order_relaxed Y Y Y

到了這里,關(guān)于《C++并發(fā)編程實(shí)戰(zhàn)》讀書筆記(4):原子變量的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • JUC并發(fā)編程學(xué)習(xí)筆記(十九)原子引用

    帶版本號的原子操作! 解決ABA問題,引入原子引用(樂觀鎖思想) AtomicStampedReference類解決ABA問題 所有相同類型的包裝類對象之間值的比較全部使用equals方法比較 Integer使用了對象緩存機(jī)制,默認(rèn)范圍是-128至127,推薦使用靜態(tài)工廠方法valueOf獲取對象實(shí)例,而不是new,因?yàn)関

    2024年02月05日
    瀏覽(22)
  • java并發(fā)編程之美第五章讀書筆記

    java并發(fā)編程之美第五章讀書筆記

    CopyOnWriteArrayList 線程安全的ArrayList,對其進(jìn)行的修改操作都是在底層的一個復(fù)制的數(shù)組(快照)進(jìn)行的,也就是寫時復(fù)制策略 類圖 每一個對象里面有一個array數(shù)組進(jìn)行存放具體的元素,ReentrantLock獨(dú)占鎖對象用來保證同時只有一個線程對array進(jìn)行修改,這里只要記得ReentrantLock是獨(dú)占鎖

    2024年02月03日
    瀏覽(20)
  • 《C++高級編程》讀書筆記(七:內(nèi)存管理)

    《C++高級編程》讀書筆記(七:內(nèi)存管理)

    1、參考引用 C++高級編程(第4版,C++17標(biāo)準(zhǔn))馬克·葛瑞格爾 2、建議先看《21天學(xué)通C++》 這本書入門,筆記鏈接如下 21天學(xué)通C++讀書筆記(文章鏈接匯總) 1. 使用動態(tài)內(nèi)存 1.1 如何描繪內(nèi)存 在本書中,內(nèi)存單元表示為一個帶有標(biāo)簽的框,該標(biāo)簽表示這個內(nèi)存對應(yīng)的變量名,方

    2024年02月08日
    瀏覽(57)
  • c++并發(fā)編程實(shí)戰(zhàn)-第4章 并發(fā)操作的同步

    c++并發(fā)編程實(shí)戰(zhàn)-第4章 并發(fā)操作的同步

    想象一種情況:假設(shè)晚上坐車外出,如何才能確保不坐過站又能使自己最輕松? 這種方式存在雙重浪費(fèi): 線程 th1(wait_for_flag)須不斷查驗(yàn)標(biāo)志,浪費(fèi)原本有用的處理時間,這部分計(jì)算資源原本可以留給其他線程使用。 線程 th1(wait_for_flag)每次循環(huán)都需要給互斥上鎖,導(dǎo)致

    2024年02月08日
    瀏覽(19)
  • 《Java并發(fā)編程實(shí)戰(zhàn)》課程筆記(十二)

    《Java并發(fā)編程實(shí)戰(zhàn)》課程筆記(十二)

    對賬系統(tǒng)的業(yè)務(wù)簡化后: 首先用戶通過在線商城下單,會生成電子訂單,保存在訂單庫; 之后物流會生成派送單給用戶發(fā)貨,派送單保存在派送單庫。 為了防止漏派送或者重復(fù)派送,對賬系統(tǒng)每天還會校驗(yàn)是否存在異常訂單。 目前對賬系統(tǒng)的處理邏輯是首先查詢訂單,然后

    2024年02月08日
    瀏覽(18)
  • 《Java并發(fā)編程實(shí)戰(zhàn)》課程筆記(九)

    《Java并發(fā)編程實(shí)戰(zhàn)》課程筆記(九)

    信號量模型還是很簡單的,可以簡單概括為:一個計(jì)數(shù)器,一個等待隊(duì)列,三個方法。 在信號量模型里,計(jì)數(shù)器和等待隊(duì)列對外是透明的,所以只能通過信號量模型提供的三個方法來訪問它們,這三個方法分別是:init()、down() 和 up()。 init():設(shè)置計(jì)數(shù)器的初始值。 down():計(jì)

    2024年02月07日
    瀏覽(21)
  • 《Java并發(fā)編程實(shí)戰(zhàn)》課程筆記(二)

    《Java并發(fā)編程實(shí)戰(zhàn)》課程筆記(二)

    在單核時代,所有的線程都是在一顆 CPU 上執(zhí)行,CPU 緩存與內(nèi)存的數(shù)據(jù)一致性容易解決。 因?yàn)樗芯€程都是操作同一個 CPU 的緩存,一個線程對緩存的寫,對另外一個線程來說一定是可見的。 一個線程對共享變量的修改,另外一個線程能夠立刻看到,我們稱為可見性。 多核

    2024年02月06日
    瀏覽(22)
  • C++ 并發(fā)編程實(shí)戰(zhàn) 第二章 線程管控

    線程通過構(gòu)建 std::thread 對象而自動啟動 ,該對象指明線程要運(yùn)行的任務(wù)。 對應(yīng)復(fù)雜的任務(wù),可以使用函數(shù)對象。 一旦啟動了線程,我們就需明確是要等待它結(jié)束(與之匯合 join() ),還是任由它獨(dú)自運(yùn)行(與之分離 detach() ) ??? 同一個線程的 .join() 方法不能被重復(fù)調(diào)用

    2023年04月08日
    瀏覽(24)
  • 并發(fā)編程-JUC-原子類

    并發(fā)編程-JUC-原子類

    JUC 整體概覽 原子類 基本類型-使用原子的方式更新基本類型 AtomicInteger:整形原子類 AtomicLong:長整型原子類 AtomicBoolean :布爾型原子類 引用類型 AtomicReference:引用類型原子類 AtomicStampedReference:原子更新引用類型里的字段原子類 AtomicMarkableReference :原子更新帶有標(biāo)記位的引

    2024年02月21日
    瀏覽(23)
  • 并發(fā)編程08:原子操作類

    并發(fā)編程08:原子操作類

    Atomic 翻譯成中文是原子的意思。在化學(xué)上,我們知道原子是構(gòu)成一般物質(zhì)的最小單位,在化學(xué)反應(yīng)中是不可分割的。在我們這里 Atomic 是指一個操作是不可中斷的。即使是在多個線程一起執(zhí)行的時候,一個操作一旦開始,就不會被其他線程干擾。 AtomicInteger :整型原子類 At

    2024年02月04日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包