前情回顾

前文我们介绍了六种内存顺序,以及三种内存模型,本文通过代码示例讲解六种内存顺序使用方法,并实现相应的内存模型。

  • 全局一致性模型

  • 同步模型(获取和释放)

  • 松散模型
    在这里插入图片描述

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%以上的内存开销。其核心优势在于结构化并发和挂起函数的协作式调…