C++并發編程-12. 用內存順序實現內存模型

前情回顧

前文我們介紹了六種內存順序,以及三種內存模型,本文通過代碼示例講解六種內存順序使用方法,并實現相應的內存模型。

  • 全局一致性模型

  • 同步模型(獲取和釋放)

  • 松散模型
    在這里插入圖片描述

memory_order_seq_cst

  • memory_order_seq_cst代表全局一致性順序,可以用于 store, load 和 read-modify-write 操作, 實現 sequencial consistent 的順序模型. 在這個模型下, 所有線程看到的所有操作都有一個一致的順序, 即使這些操作可能針對不同的變量, 運行在不同的線程.

std::atomic<bool> x, y;
std::atomic<int> z;
void write_x_then_y() {x.store(true, std::memory_order_relaxed);  // 1y.store(true, std::memory_order_relaxed);  // 2
}
void read_y_then_x() {while (!y.load(std::memory_order_relaxed)) { // 3std::cout << "y load false" << std::endl;}if (x.load(std::memory_order_relaxed)) { //4++z;}
}
void TestOrderRelaxed() {std::thread t1(write_x_then_y);std::thread t2(read_y_then_x);t1.join();t2.join();assert(z.load() != 0); // 5
}

上面的代碼load和store都采用的是memory_order_relaxed。線程t1按次序執行1和2,但是線程t2看到的可能是y為true,x為false。進而導致TestOrderRelaxed觸發斷言z為0.
如果換成memory_order_seq_cst則能保證所有線程看到的執行順序是一致的。


void write_x_then_y() {x.store(true, std::memory_order_seq_cst);  // 1y.store(true, std::memory_order_seq_cst);  // 2
}
void read_y_then_x() {while (!y.load(std::memory_order_seq_cst)) { // 3std::cout << "y load false" << std::endl;}if (x.load(std::memory_order_seq_cst)) { //4++z;}
}
void TestOrderSeqCst() {std::thread t1(write_x_then_y);std::thread t2(read_y_then_x);t1.join();t2.join();assert(z.load() != 0); // 5
}

上面的代碼x和y采用的是memory_order_seq_cst, 所以當線程t2執行到3處并退出循環時我們可以斷定y為true,因為是全局一致性順序,所以線程t1已經執行完2處將y設置為true,那么線程t1也一定執行完1處代碼并對t2可見,所以當t2執行至4處時x為true,那么會執行z++保證z不為零,從而不會觸發斷言。

實現 sequencial consistent 模型有一定的開銷. 現代 CPU 通常有多核, 每個核心還有自己的緩存. 為了做到全局順序一致, 每次寫入操作都必須同步給其他核心. 為了減少性能開銷, 如果不需要全局順序一致, 我們應該考慮使用更加寬松的順序模型.

memory_order_relaxed

memory_order_relaxed 可以用于 store, load 和 read-modify-write 操作, 實現 relaxed 的順序模型.
前文我們介紹過這種模型下, 只能保證操作的原子性和修改順序 (modification order) 一致性, 無法實現 synchronizes-with 的關系。

void TestOrderRelaxed() {std::atomic<bool> rx, ry;std::thread t1([&]() {rx.store(true, std::memory_order_relaxed); // 1ry.store(true, std::memory_order_relaxed); // 2});std::thread t2([&]() {while (!ry.load(std::memory_order_relaxed)); //3assert(rx.load(std::memory_order_relaxed)); //4});t1.join();t2.join();
}

在這里插入圖片描述

Acquire-Release

在這里插入圖片描述

oid TestReleaseAcquire() {std::atomic<bool> rx, ry;std::thread t1([&]() {rx.store(true, std::memory_order_relaxed); // 1ry.store(true, std::memory_order_release); // 2});std::thread t2([&]() {while (!ry.load(std::memory_order_acquire)); //3assert(rx.load(std::memory_order_relaxed)); //4});t1.join();t2.join();
}

在這里插入圖片描述
在這里插入圖片描述

Release sequences

我們再考慮一種情況,多個線程對同一個變量release操作,另一個線程對這個變量acquire,那么只有一個線程的release操作喝這個acquire線程構成同步關系。

void ReleasAcquireDanger2() {std::atomic<int> xd{0}, yd{ 0 };std::atomic<int> zd;std::thread t1([&]() {xd.store(1, std::memory_order_release);  // (1)yd.store(1, std::memory_order_release); //  (2)});std::thread t2([&]() {yd.store(2, std::memory_order_release);  // (3)});std::thread t3([&]() {while (!yd.load(std::memory_order_acquire)); //(4)assert(xd.load(std::memory_order_acquire) == 1); // (5)});t1.join();t2.join();t3.join();
}

(2)和(4) ,(3)和(4)都可以構成同步關系
在這里插入圖片描述


void ReleaseSequence() {std::vector<int> data;std::atomic<int> flag{ 0 };std::thread t1([&]() {data.push_back(42);  //(1)flag.store(1, std::memory_order_release); //(2)});std::thread t2([&]() {int expected = 1;while (!flag.compare_exchange_strong(expected, 2, std::memory_order_relaxed)) // (3)expected = 1;});std::thread t3([&]() {while (flag.load(std::memory_order_acquire) < 2); // (4)assert(data.at(0) == 42); // (5)});t1.join();t2.join();t3.join();
}

在這里插入圖片描述

memory_order_consume

在這里插入圖片描述

void ConsumeDependency() {std::atomic<std::string*> ptr;int data;std::thread t1([&]() {std::string* p = new std::string("Hello World"); // (1)data = 42; // (2)ptr.store(p, std::memory_order_release); // (3)});std::thread t2([&]() {std::string* p2;while (!(p2 = ptr.load(std::memory_order_consume))); // (4)assert(*p2 == "Hello World"); // (5)assert(data == 42); // (6)});t1.join();t2.join();
}

在這里插入圖片描述

單例模式改良

還記得我們之前用智能指針雙重檢測方式實現的單例模式嗎?我當時說過是存在線程安全問題的,看看下面這段單例模式

//利用智能指針解決釋放問題
class SingleAuto
{
private:SingleAuto(){}SingleAuto(const SingleAuto&) = delete;SingleAuto& operator=(const SingleAuto&) = delete;
public:~SingleAuto(){std::cout << "single auto delete success " << std::endl;}static std::shared_ptr<SingleAuto> GetInst(){// 1 處if (single != nullptr){return single;}// 2 處s_mutex.lock();// 3 處if (single != nullptr){s_mutex.unlock();return single;}// 4處single = std::shared_ptr<SingleAuto>(new SingleAuto);s_mutex.unlock();return single;}
private:static std::shared_ptr<SingleAuto> single;static std::mutex s_mutex;
};

在這里插入圖片描述
為了解決這個問題,我們可以通過內存模型來解決


//利用智能指針解決釋放問題
class SingleMemoryModel
{
private:SingleMemoryModel(){}SingleMemoryModel(const SingleMemoryModel&) = delete;SingleMemoryModel& operator=(const SingleMemoryModel&) = delete;
public:~SingleMemoryModel(){std::cout << "single auto delete success " << std::endl;}static std::shared_ptr<SingleMemoryModel> GetInst(){// 1 處if (_b_init.load(std::memory_order_acquire)){return single;}// 2 處s_mutex.lock();// 3 處if (_b_init.load(std::memory_order_relaxed)) // 這里可以使用寬松的模型,因為上面的已經加鎖了,鎖的權力是最大的{s_mutex.unlock();return single;}// 4處single = std::shared_ptr<SingleMemoryModel>(new SingleMemoryModel);_b_init.store(true, std::memory_order_release);s_mutex.unlock();return single;}
private:static std::shared_ptr<SingleMemoryModel> single;static std::mutex s_mutex;static std::atomic<bool> _b_init ;
};
std::shared_ptr<SingleMemoryModel> SingleMemoryModel::single = nullptr;
std::mutex SingleMemoryModel::s_mutex;
std::atomic<bool> SingleMemoryModel::_b_init = false;

總結

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88227.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88227.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88227.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AI測試革命:從智能缺陷檢測到自愈式測試框架的工業實踐

AI測試革命&#xff1a;從智能缺陷檢測到自愈式測試框架的工業實踐 希望對大家有用&#xff01; 目錄AI測試革命&#xff1a;從智能缺陷檢測到自愈式測試框架的工業實踐希望對大家有用&#xff01;一、傳統測試之殤&#xff1a;工業質檢的切膚之痛二、智能缺陷檢測系統架構1. …

二、深度學習——損失函數

二、損失函數損失函數定義&#xff1a;損失函數是用來衡量模型參數的質量的函數&#xff0c;衡量方式是比較網絡輸出和真實輸出的差異別名&#xff1a;損失函數&#xff08;loss function&#xff09;&#xff0c;代價函數&#xff08;cost function&#xff09;&#xff0c;目…

面向數據報的套接字通道技術詳解

數據報通道基礎 通道特性與創建方式 java.nio.channels.DatagramChannel類實例代表數據報通道&#xff0c;默認處于阻塞模式。通過configureBlocking(false)方法可將其配置為非阻塞模式。創建數據報通道需調用其靜態open()方法&#xff0c;若用于IP組播則需指定組播組的地址類型…

147.在 Vue3 中使用 OpenLayers 地圖上 ECharts 模擬飛機循環飛行

&#x1f9e9; 效果預覽 &#x1f447; 飛機從多個城市起飛并向其他城市飛行&#xff0c;動畫流暢&#xff0c;地圖可縮放拖拽&#xff1a; &#x1f4e6; 一、項目技術棧 技術用途Vue 3現代前端框架OpenLayers地圖底圖渲染ECharts ol-echarts飛機飛行動畫渲染ol-echarts將 …

OCR與PDF解析的區別

我們日常所接觸的文檔中&#xff0c;經常能碰到多語言混合的文檔。比如論文試卷、財報研報、跨國票據都含有多種語言和文字。要將文檔中的內容識別并提取務必需要使用到OCR技術&#xff0c;而傳統的OCR工具在處理這類型文檔的時候有局限性。早期的 OCR 系統識別精度有限&#x…

Java 單例類詳解:從基礎到高級,掌握線程安全與高效設計

作為一名Java開發工程師&#xff0c;你一定對**單例模式&#xff08;Singleton Pattern&#xff09;**不陌生。它是23種經典設計模式中最簡單也是最常用的一種&#xff0c;用于確保一個類在整個應用程序中只有一個實例存在。單例廣泛應用于系統配置、數據庫連接池、日志管理器、…

面向對象設計

你列出的這些屬于 C 高級開發中面向對象設計與架構設計的核心知識&#xff0c;也是面試高級工程師崗位必問的內容。下面我按順序&#xff0c;深入講解每一項概念、原理、用途&#xff0c;并穿插 C 示例。? 1. 設計原則&#xff08;SOLID&#xff09;SOLID 是面向對象設計的五大…

IntelliJ IDEA讓我的開發效率翻倍:從新手到高效開發者的進階之路

IntelliJ IDEA讓我的開發效率翻倍&#xff1a;從新手到高效開發者的進階之路 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 總有一行代碼&#xff0c;能點亮萬千星辰。 &#x1f50d; 在技術的宇宙中&#xff0c;我愿做永不停歇的探索者。 ? 用…

css sprites使用

CSS Sprites 是一種將多個小圖標或背景圖像合并到一個大圖中的技術。通過減少HTTP請求次數&#xff0c;可以顯著提高頁面加載速度。其核心原理是&#xff1a;通過設置元素的背景圖&#xff08;background-image&#xff09;為這個大圖&#xff0c;然后調整背景位置&#xff08;…

分布式爬蟲在電商平臺商品數據大規模采集中的技術應用

在電商平臺商品數據大規模采集場景中&#xff0c;分布式爬蟲憑借其高效、可擴展、抗風險的特性&#xff0c;成為突破單節點爬蟲性能瓶頸的核心技術方案。以下從技術架構、關鍵技術點、電商場景適配及挑戰應對四個維度&#xff0c;解析其具體應用&#xff1a;一、分布式爬蟲的核…

Linux的`if test`和`if [ ]中括號`的取反語法比較 筆記250709

Linux的if test和if 中括號的取反語法比較 筆記250709 Linux的 test命令&#xff08;或等價中括號寫法 [空格expression空格]&#xff09;的用法詳解. 筆記250709 四種取反語法: if ! test -e xxx ;then... 和 if test ! -e xxx ;then... 和 if ! [ -e xxx ] ;then... 和 if …

記錄使用ubuntu16.04編譯aosp(android8.1與10)遇到的問題

一、前言&#xff1a; 本來打算用wsl來編譯AOSP&#xff0c;但是折騰了好幾天&#xff0c;以失敗告終。后來使用vmware反而成功了。 本篇同樣會把wsl遇到的問題與嘗試記錄下來。 環境&#xff1a;vmware ubuntu16.04。 為什么會使用ubuntu16.04呢&#xff0c;因為在公司有一…

hiredis window之RFDMap

簡介 RFDMap用于將socket分配映射成連續的文件描述符&#xff0c;同時管理回收的文件描述符&#xff0c;因為ae構架中管理fd與對應事件處理器使用的是數據&#xff0c;fd作為數組下標 結構 #mermaid-svg-zQz2LTrKRi0LQTII {font-family:"trebuchet ms",verdana,arial…

RustFS一款Rust 驅動的 高性能 分布式存儲系統

演示地址&#xff1a;https://play.rustfs.com/browser 訪問賬號&#xff08;默認 rustfsadmin&#xff09;。 訪問密鑰&#xff08;默認 rustfsadmin&#xff09;。 下載mc https://dl.min.io/client/mc/release可以直接在 Linux 系統上安裝 mc&#xff08;&#xff0c;然后訪…

微軟 Bluetooth LE Explorer 實用工具的詳細使用分析

微軟 Bluetooth LE Explorer 實用工具的詳細使用分析 文章目錄 微軟 **Bluetooth LE Explorer** 實用工具的詳細使用分析1. **工具定位與核心功能**2. **關鍵特性與更新**3. **使用場景示例**4. **系統要求與依賴**5. **與專業工具對比**6. **局限性**7. **實踐建議**結論以下是…

centos 7.6安裝mysql8

在 CentOS 7.6 上安裝 MySQL 8.0.42 的步驟如下&#xff0c;基于搜索結果中的最新信息&#xff1a; 下載 MySQL 8.0.42 安裝包 https://dev.mysql.com/downloads/mysql/從 MySQL 官方網站下載 mysql-8.0.42-1.el7.x86_64.rpm-bundle.tar 文件&#xff1a; 官方下載地址&#xf…

CentOS7更換阿里云yum源

問題&#xff1a;剛剛在本地安裝了CentOS7虛擬機&#xff0c;使用yum安裝vim軟件時&#xff08;最小化安裝只有vi沒有vim&#xff09;出現下面的報錯原因 &#xff1a;CentOS7 已于2024-6-30停止維護&#xff0c;官方鏡像源已不可用&#xff0c;可以更換為阿里云鏡像源解決&…

UE5內置插件 AnimToTexture 簡單入門

開啟插件 首先安裝插件&#xff0c;然后重啟。打開顯示插件內容我們就可以找到插件自帶的轉換內容將骨骼網格體轉換為頂點動畫有兩種方式&#xff1a; 最簡單的記錄每個頂點的位置然后通過切換拾取顏色偏移實現記錄骨骼的變換&#xff0c;然后通過貼圖去修改骨骼位置計算 這兩種…

如何搭建Appium環境?

&#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快1、安裝Java Development Kit&#xff08;JDK&#xff09;前往Oracle官網下載JDK。在https://www.oracle.com/java/technologies/javase-jdk11-downloads.html 找到…

Android kotlin 協程的詳細使用指南

Android Kotlin 協程的詳細使用指南&#xff0c;結合核心概念、實戰場景和最佳實踐&#xff1a;一、協程基礎概念?協程本質?協程是輕量級線程&#xff0c;通過掛起/恢復機制實現并發&#xff0c;相比線程節省90%以上的內存開銷。其核心優勢在于結構化并發和掛起函數的協作式調…