亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解c++ atomic原子編程中的Memory Order

 更新時(shí)間:2021年06月07日 09:53:09   作者:可可西  
在多核編程中,我們使用內(nèi)核對(duì)象【如:事件對(duì)象(Event)、互斥量對(duì)象(Mutex,或互斥體對(duì)象)、信號(hào)量對(duì)象(Semaphore)等】來避免多個(gè)線程修改同一個(gè)數(shù)據(jù)時(shí)產(chǎn)生的競(jìng)爭(zhēng)條件。本文將詳細(xì)介紹c++ atomic原子編程中的Memory Order。

概述

但是,基于內(nèi)核對(duì)象的同步,會(huì)帶來昂貴的上下文切換(用戶態(tài)切換到內(nèi)核態(tài),占用1000個(gè)以上的cpu周期)。就需要使用另一種方法 —— 原子指令。

僅靠原子技術(shù)實(shí)現(xiàn)不了對(duì)資源的訪問控制,即使簡(jiǎn)單計(jì)數(shù)操作,看上去正確的代碼也可能會(huì)crash。

這里的關(guān)鍵在于編譯器和cpu實(shí)施的重排指令導(dǎo)致了讀寫順序的變化。只要沒有依賴,代碼中在后面的指令就可能跑到前面去,編譯器和CPU都會(huì)這么做。

注1:?jiǎn)尉€程代碼不需要關(guān)心亂序的問題。因?yàn)閬y序至少要保證這一原則:不能改變單線程程序的執(zhí)行行為

注2:內(nèi)核對(duì)象多線程編程在設(shè)計(jì)的時(shí)候都阻止了它們調(diào)用點(diǎn)中的亂序(已經(jīng)隱式包含memory barrier),不需要考慮亂序的問題。

注3:使用用戶模式下的線程同步時(shí),亂序的效果才會(huì)顯露無疑。

程序員可以使用c++11 atomic提供了6種memory order,來在編程語言層面對(duì)編譯器和cpu實(shí)施的重排指令行為進(jìn)行控制

多線程編程時(shí),通過這些標(biāo)志位,來讀寫原子變量,可以組合出4種同步模型:

Relaxed ordering

Release-Acquire ordering

Release-Consume ordering

Sequentially-consistent ordering

默認(rèn)情況下,std::atomic使用的是Sequentially-consistent ordering(最嚴(yán)格的同步模型)。但在某些場(chǎng)景下,合理使用其它3種ordering,可以讓編譯器優(yōu)化生成的代碼,從而提高性能。

Relaxed ordering

在這種模型下,std::atomic的load()和store()都要帶上memory_order_relaxed參數(shù)。Relaxed ordering僅僅保證load()和store()是原子操作,除此之外,不提供任何跨線程的同步。

先看看一個(gè)簡(jiǎn)單的例子:

std::atomic<int> x = 0;     // global variable
std::atomic<int> y = 0;     // global variable
		  
Thread-1:                                  Thread-2:
r1 = y.load(memory_order_relaxed); // A    r2 = x.load(memory_order_relaxed); // C
x.store(r1, memory_order_relaxed); // B    y.store(42, memory_order_relaxed); // D

執(zhí)行完上面的程序,可能出現(xiàn)r1 == r2 == 42。理解這一點(diǎn)并不難,因?yàn)榫幾g器允許調(diào)整 C 和 D 的執(zhí)行順序。

如果程序的執(zhí)行順序是 D -> A -> B -> C,那么就會(huì)出現(xiàn)r1 == r2 == 42。

如果某個(gè)操作只要求是原子操作,不需要其它同步的保障,就可以使用 Relaxed ordering。程序計(jì)數(shù)器是一種典型的應(yīng)用場(chǎng)景。

#include <cassert>
#include <vector>
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> cnt = {0};
void f()
{
    for (int n = 0; n < 1000; ++n) {
        cnt.fetch_add(1, std::memory_order_relaxed);
    }
}
int main()
{
    std::vector<std::thread> v;
    for (int n = 0; n < 10; ++n) {
        v.emplace_back(f);
    }
    for (auto& t : v) {
        t.join();
    }
    assert(cnt == 10000);    // never failed
    return 0;
}

Release-Acquire ordering

在這種模型下,store()使用memory_order_release,而load()使用memory_order_acquire。這種模型有兩種效果,第一種是可以限制 CPU 指令的重排:

(1)在store()之前的所有讀寫操作,不允許被移動(dòng)到這個(gè)store()的后面。 // write-release語義

(2)在load()之后的所有讀寫操作,不允許被移動(dòng)到這個(gè)load()的前面。 // read-acquire語義

該模型可以保證:如果Thread-1的store()的那個(gè)值,成功被 Thread-2的load()到了,那么 Thread-1在store()之前對(duì)內(nèi)存的所有寫入操作,此時(shí)對(duì) Thread-2 來說,都是可見的。

下面的例子闡述了這種模型的原理:

#include <thread>
#include <atomic>
#include <cassert>
#include <string>
std::atomic<bool> ready{ false };
int data = 0;
void producer()
{
    data = 100;                                       // A
    ready.store(true, std::memory_order_release);     // B
}
void consumer()
{
    while (!ready.load(std::memory_order_acquire))    // C
        ;
    assert(data == 100); // never failed              // D
}
int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

讓我們分析一下這個(gè)過程:

首先 A 不允許被移動(dòng)到 B 的后面。

同樣 D 也不允許被移動(dòng)到 C 的前面。

當(dāng) C 從 while 循環(huán)中退出了,說明 C 讀取到了 B store()的那個(gè)值,此時(shí),Thread-2 保證能夠看見 Thread-1 執(zhí)行 B 之前的所有寫入操作(也即是 A)。

使用Release-Acquire ordering實(shí)現(xiàn)雙重檢查鎖模式(DLCP)

下面單件為例來說明:

class Singleton
{
public:
    static Singleton* get_instance() {
        Singleton* tmp = instance_.load(std::memory_order_acquire);
        if (tmp == nullptr) {
            std::unique_lock<std::mutex> lk(mutex_);
            tmp = instance_;
            if (tmp == nullptr) {
                tmp = new Singleton();
                instance_.store(std::memory_order_release);
            }
        }
        return tmp;
    }

private:
    Singleton() = default;
    static std::atomic<Singleton*> instance_;
    static std::mutex mutex_;
};

使用Release-Acquire ordering實(shí)現(xiàn)自旋鎖(Spinlock)

獲取和釋放語義,是實(shí)現(xiàn)鎖的基礎(chǔ)(Spinlock, Mutex, RWLock, ...),所有被[Read Acquire,Write Release]包含的區(qū)域,即構(gòu)成了一個(gè)臨界區(qū),臨界區(qū)里的內(nèi)存操作,不會(huì)亂序到臨界區(qū)之外執(zhí)行。

            read-acquire(判斷是否加鎖,沒則加鎖,否則循環(huán)等待)

-------------------------------------------------------------------------

            all memory operation stay between the line(臨界區(qū))

-------------------------------------------------------------------------

                        write-release(釋放鎖)

實(shí)現(xiàn)代碼如下:

#include <atomic>
class simple_spin_lock
{
public:
    simple_spin_lock() = default;
    void lock()
    {
        while (flag.test_and_set(std::memory_order_acquire))
            continue;
    }
    void unlock()
    {
        flag.clear(std::memory_order_release);
    }
private:
    simple_spin_lock(const simple_spin_lock&) = delete;
    simple_spin_lock& operator =(const simple_spin_lock&) = delete;
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
};

①對(duì)std::atomic_flag的操作具有原子性,保證了同一時(shí)間,只有一個(gè)線程能夠lock成功,其余線程全部在while循環(huán)

②使用了acquire內(nèi)存屏障, 所以lock具有獲取語義

③使用了release內(nèi)存屏障, 所以u(píng)nlock具有釋放語義

Release-Consume ordering

在這種模型下,store()使用memory_order_release,而load()使用memory_order_consume。這種模型有兩種效果,第一種是可以限制 CPU 指令的重排:

(1)在store()之前的所有讀寫操作,不允許被移動(dòng)到這個(gè)store()的后面。

(2)在load()之后的所有依賴此原子變量的讀寫操作,不允許被移動(dòng)到這個(gè)load()的前面。

注:不依賴此原子變量的讀寫操作可能會(huì)CPU指令重排

下面的例子闡述了這種模型的原理:

#include <thread>
#include <atomic>
#include <cassert>
#include <string>

std::atomic<std::string*> ptr;
int data;
// thread1
void producer()
{
    std::string* p  = new std::string("Hello"); // A
    data = 42; // B
    ptr.store(p, std::memory_order_release); // C
}
// thread2
void consumer()
{
    std::string* p2;
    while (!(p2 = ptr.load(std::memory_order_consume))) // D
        ;
    assert(*p2 == "Hello"); //E     always true: *p2 carries dependency from ptr
    assert(data == 42); // F     may be false: data does not carry dependency from ptr
}

int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join(); 
    t2.join();
    return 0;
}

Sequentially-consistent ordering

所有以memory_order_seq_cst為參數(shù)的原子操作(不限于同一個(gè)原子變量),對(duì)所有線程來說有一個(gè)全局順序(total order)

并且兩個(gè)相鄰memory_order_seq_cst原子操作之間的其他操作(包括非原子變量操作),不能reorder到這兩個(gè)相鄰操作之外

UE4下的Memory Order

enum class EMemoryOrder
{
    // Provides no guarantees that the operation will be ordered relative to any other operation.
    Relaxed,

    // Establishes a single total order of all other atomic operations marked with this.
    SequentiallyConsistent  // Load和Store函數(shù)缺省為該類型
};

詳見:UnrealEngine\Engine\Source\Runtime\Core\Public\Templates\Atomic.h

Atomic相關(guān)的測(cè)試代碼見:UnrealEngine\Engine\Source\Runtime\Core\Private\Tests\Misc\AtomicTest.cpp

以上就是詳解c++ atomic原子編程中的Memory Order的詳細(xì)內(nèi)容,更多關(guān)于c++ atomic原子編程中的Memory Order的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 70行C語言代碼實(shí)現(xiàn)貪吃蛇

    70行C語言代碼實(shí)現(xiàn)貪吃蛇

    這篇文章主要為大家詳細(xì)介紹了70行C語言代碼實(shí)現(xiàn)貪吃蛇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 詳解C/C++性能優(yōu)化背后的方法論TMAM

    詳解C/C++性能優(yōu)化背后的方法論TMAM

    開發(fā)過程中我們多少都會(huì)關(guān)注服務(wù)的性能,然而性能優(yōu)化是相對(duì)比較困難,往往需要多輪優(yōu)化、測(cè)試,屬于費(fèi)時(shí)費(fèi)力,有時(shí)候還未必有好的效果。但是如果有較好的性能優(yōu)化方法指導(dǎo)、工具輔助分析可以幫助我們快速發(fā)現(xiàn)性能瓶頸所在,針對(duì)性地進(jìn)行優(yōu)化,可以事半功倍
    2021-06-06
  • C語言實(shí)現(xiàn)軍旗游戲的示例代碼

    C語言實(shí)現(xiàn)軍旗游戲的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用C語言實(shí)現(xiàn)軍旗游戲,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-11-11
  • C++中的函數(shù)指針與函數(shù)對(duì)象的總結(jié)

    C++中的函數(shù)指針與函數(shù)對(duì)象的總結(jié)

    以下是對(duì)C++中的函數(shù)指針與函數(shù)對(duì)象的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以參考下
    2013-07-07
  • Qt繪制時(shí)鐘效果

    Qt繪制時(shí)鐘效果

    這篇文章主要為大家詳細(xì)介紹了Qt繪制時(shí)鐘效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • C++實(shí)現(xiàn)LeetCode(66.加一運(yùn)算)

    C++實(shí)現(xiàn)LeetCode(66.加一運(yùn)算)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(66.加一運(yùn)算),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C++類與對(duì)象之日期類的實(shí)現(xiàn)

    C++類與對(duì)象之日期類的實(shí)現(xiàn)

    這篇文章主要介紹如何實(shí)現(xiàn)C++中的日期類相關(guān)資料,需要的朋友可以參考下面文章的具體內(nèi)容
    2021-09-09
  • 如何利用Emacs來調(diào)試C++程序

    如何利用Emacs來調(diào)試C++程序

    本文給大家分享的是使用編輯器中的神器Emacs來調(diào)試C++程序的方法,非常的細(xì)致全面,有需要的小伙伴可以參考下
    2016-03-03
  • C++排序算法之插入排序解析

    C++排序算法之插入排序解析

    這篇文章主要介紹了C++排序算法之插入排序解析,將數(shù)組分為有序表和無序表,每次從有序表中取出一個(gè)元素,插入到有序表的適當(dāng)位置,每遍歷一次,有序表中元素增加一個(gè),無序表中元素個(gè)數(shù)減少一個(gè),重復(fù)n-1次,完成排序,需要的朋友可以參考下
    2023-10-10
  • C++插件化 NDD源碼的插件機(jī)制實(shí)現(xiàn)解析

    C++插件化 NDD源碼的插件機(jī)制實(shí)現(xiàn)解析

    這篇文章主要介紹了C++插件化 NDD源碼的插件機(jī)制實(shí)現(xiàn)解析,這里再介紹推薦下優(yōu)秀的國(guó)產(chǎn)軟件開源項(xiàng)目?NDD(notepad--),一個(gè)支持windows/linux/mac的文本編輯器,目標(biāo)是要國(guó)產(chǎn)替換同類軟件,需要的朋友可以參考下
    2023-03-03

最新評(píng)論