当前位置: 首页 > news >正文

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.dtcms.com/a/273274.html

相关文章:

  • 写《XX顶层设计》和《XX可研报告》区别。
  • MySQL索引:数据库的超级目录
  • 网站文章更新慢影响排名?AI批量写文章技巧分享
  • 综合演练——名片管理系统I
  • Canvas 状态管理 语法糖 canvas.withSave() {}
  • AtCoder Beginner Contest 413
  • 并发编程原理与实战(十六)深入锁的演进,为什么有了synchronized还需要Lock?
  • UECC-UE连接协调的运作方式
  • (一)OpenCV——噪声去除(降噪)
  • React--Fiber 架构
  • 数据库操作核心知识点整理
  • mac m1芯片 安装pd及win10系统
  • 第12讲—一元函数积分学的物理应用
  • 在vscode中安装jupyter
  • 电机电角度与机械角度的个人理解:从蒙圈到理解到放弃
  • 蓝桥云课 矩形切割-Java
  • 快速分页wpf
  • npx cowsay 让动物说话~
  • Java重试+事务的方式优化saveBatch,配置信息修改
  • Flink Exactly Once 和 幂等
  • 【郑大二年级信安小学期】Day9:XSS跨站攻击XSS绕过CSRF漏洞SSRF漏洞
  • 服务器深夜告警?可能是攻击前兆!
  • Unity插件——ABC详解
  • AI驱动的低代码革命:解构与重塑开发范式
  • LeetCode 8. 字符串转换整数 (atoi)
  • 【保姆级喂饭教程】idea中安装Conventional Commit插件
  • FreeRTOS—任务创建和删除的API函数和方法
  • 书生实训营第二关:大模型对战
  • 列表初始化
  • C++ Lambda 表达式详解