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

C++并发编程-14. 利用栅栏实现同步

前文我们通过原子操作实战实现了无锁队列,今天完善一下无锁的原子操作剩余的知识,包括Relaese和Acquire内存序在什么情况下是存在危险的,以及我们可以利用栅栏机制实现同步等等。

线程可见顺序

  • 我们提到过除了memory_order_seq_cst顺序,其他的顺序都不能保证原子变量修改的值在其他多线程中看到的顺序是一致的。

但是可以通过同步机制保证一个线程对原子变量的修改对另一个原子变量可见。通过“Syncronizes With” 的方式达到先行的效果。

但是我们说的先行是指 “A Syncronizes With B ”, 如果A 的结果被B读取,则A 先行于B。

有时候我们线程1对A的store操作采用release内存序,而线程2对B的load采用acquire内存序,并不能保证A 一定比 B先执行。因为两个线程并行执行无法确定先后顺序,我们指的先行不过是说如果B读取了A操作的结果,则称A先行于B。

我们看下面的一段案例

#include <iostream>
#include <atomic>
#include <thread>
#include <cassert>
std::atomic<bool> x, y;
std::atomic<int> z;
void write_x()
{x.store(true, std::memory_order_release); //1
}
void write_y()
{y.store(true, std::memory_order_release); //2
}
void read_x_then_y()
{while (!x.load(std::memory_order_acquire));if (y.load(std::memory_order_acquire))   //3++z;
}
void read_y_then_x()
{while (!y.load(std::memory_order_acquire));if (x.load(std::memory_order_acquire))   //4++z;
}
// 我们写一个函数测试,函数TestAR中初始化x和y为false, 启动4个线程a,b,c,d,分别执行write_x, write_y, read_x_then_y, read_y_then_x.
void TestAR()
{x = false;y = false;z = 0;std::thread a(write_x);std::thread b(write_y);std::thread c(read_x_then_y);std::thread d(read_y_then_x);a.join();b.join();c.join();d.join();assert(z.load() != 0); //5std::cout << "z value is " << z.load() << std::endl;
}

有的读者可能会觉5处的断言不会被触发,他们认为c和d肯定会有一个线程对z执行++操作。他们的思路是这样的。
1 如果c线程执行read_x_then_y没有对z执行加加操作,那么说明c线程读取的x值为true, y值为false。
2 之后d线程读取时,如果保证执行到4处说明y为true,等d线程执行4处代码时x必然为true。
3 他们的理解是如果x先被store为true,y后被store为true,c线程看到y为false时x已经为true了,那么d线程y为true时x也早就为true了,所以z一定会执行加加操作。

上述理解是不正确的,我们提到过即便是releas和acquire顺序也不能保证多个线程看到的一个变量的值是一致的,更不能保证看到的多个变量的值是一致的。

变量x和y的载入操作3和4有可能都读取false值(与宽松次序的情况一样),因此有可能令断言触发错误。变量x和y分别由不同线程写出,所以两个释放操作都不会影响到对方线程。

看下图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

即线程a执行了,但是线程d没有看见
线程b执行了,但是线程c没有看见

栅栏

有时候我们可以通过栅栏保证指令编排顺序。

看下面一段代码

#include <atomic>
#include <thread>
#include <assert.h>
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));  // 3if(x.load(std::memory_order_relaxed))  // 4++z;
}
int main()
{x=false;y=false;z=0;std::thread a(write_x_then_y);std::thread b(read_y_then_x);a.join();b.join();assert(z.load()!=0);  //5
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/286794.html

相关文章:

  • 嵌入式系统内核镜像相关(十六)
  • Vue中使用vue-3d-model实现加载3D模型预览展示
  • docker命令参数详解
  • 数字化转型:概念性名词浅谈(第三十二讲)
  • 基础密码协议
  • Python os 模块:系统操作的 “百宝箱”
  • Java编程规范(简约版)
  • MoE,混合专家
  • pycharm结构查看器
  • 世界有色金属杂志世界有色金属杂志社世界有色金属编辑部2025年第9期目录
  • WAF能够解决数据库被渗透的问题吗?
  • Redis-集群与分区
  • 5W8-3D牢游戏超级大集合[2012年6月] 地址 + 解压密码
  • 更适合后端宝宝的前端三件套之HTML
  • 光伏系统优化布局,实现从空间利用到效能的最大化
  • 2-大语言模型—理论基础:详解Transformer架构的实现(2)
  • Redisson 分布式锁
  • 一小时学习Redis
  • 使用 jar -xvf 解压JAR文件无反应怎么办?
  • Maven私服仓库,发布jar到私服仓库,依赖的版本号如何设置,规范是什么
  • 帆软可视化图
  • mave手动下载某个依赖,到本地库
  • 更适合后端宝宝的前端三件套之JavaScript
  • /字符串/
  • 《每日AI-人工智能-编程日报》--2025年7月18日
  • Simulink 按位控制的控制DO信号变量转换为uint16类型的控制字
  • Flux Kontext Lora 模型训练环境搭建
  • 软件维护全维度解析:从修复到进化的生命周期管理
  • linux制作镜像、压缩镜像、烧录的方法
  • 虚拟机centos服务器安装