首页 > 系统相关 >C++内存序

C++内存序

时间:2023-05-06 23:44:06浏览次数:38  
标签:std load read C++ --- 内存 memory order

先后一致次序(memory_order_seq_cst)

如果程序服从先后一致次序,就简单地把一切事件视为按先后顺序发生,其操作与这种次序保持一致。假设在多线程程序的全部原子类型的实例上,所有的操作都保持先后一致,name它们将按某种特定次序改由单线程执行,则俩个程序的操作毫无区别。

缺点:在弱保序的多处理器计算机上,保序操作会带来很严重的性能损失。

例子:

#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;

void write_x()
{
    x.store(true,std::memory_order_seq_cst);    ⇽---  ①
}

void write_y()
{
    y.store(true,std::memory_order_seq_cst);    ⇽---  ②
}

void read_x_then_y()
{
    while(!x.load(std::memory_order_seq_cst));
    if(y.load(std::memory_order_seq_cst))    ⇽---  ③
        ++z;
}

void read_y_then_x()
{
    while(!y.load(std::memory_order_seq_cst));
    if(x.load(std::memory_order_seq_cst))    ⇽---  ④
        ++z;
}

int main()
{
    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);    ⇽---  ⑤
}

 如何理解这个例子呢,关键点还是要理解原子变量。原子变量的意思就是,这个变量load进寄存器和store进内存,在代码里的顺序和真正执行的顺序要保持一致。所以我们先看x, x在write_x(先执行)和read_x_then_y(后执行),这两个函数里出现了。因为在代码的顺序是先write_x中出现,然后read_x_then_y, 所以所有代码的执行顺序是x.store(ture)   -》 (在write_x()中), x.load() 和 y.load() -》(在read_x_then_y)中,如果是y先执行。注意每个线程里的代码执行顺序都没有变。这个就是强序带来的。

 

获取-释放次序(memory_order_cosume, memory_order_acquire, memory_order_release和memory_order_acq_rel)

获取-释放次序比宽松次序严格一些,它会产生一定程度的同步效果,而不会形成先后一致次序的全局总操作序列,在该内存模型中,原子化载入即为获取操作(memory_order_acquire),原子化存储即为释放操作(memory_order_release),而原子化“读改写”操作(像fetch_add()和exchange())则为获取或释放操作,或两者都是(memory_order_acq_rel)。这种内存次序在成对的读写线程之间起到同步作用。释放和获取操作构成同步关系,前者写出的值由后者读取。换言之,若多个线程从获取释放-操作构成同步关系,则其缩减的操作序列可能差异,但差异的程度和方式都受到一定条件的制约。

例子:

宽松情况下

#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x()
{
    x.store(true,std::memory_order_release);
}
void write_y()
{
    y.store(true,std::memory_order_release);
}
void read_x_then_y()
{
    while(!x.load(std::memory_order_acquire));
    if(y.load(std::memory_order_acquire))    ⇽---  ①
        ++z;
}
void read_y_then_x()
{
    while(!y.load(std::memory_order_acquire));
    if(x.load(std::memory_order_acquire))    ⇽---  ②
        ++z;
}
int main()
{
    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);    ⇽---  ③
}

 上述图中,什么情况都有可能发生。

获取-释放次序下,线程会有同步情况

例子:

#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);    ⇽---  ①
    y.store(true,std::memory_order_release);    ⇽---  ②
}
void read_y_then_x()
{
    while(!y.load(std::memory_order_acquire));    ⇽---  ③以自旋方式等待变量y的值设置为true
    if(x.load(std::memory_order_relaxed))    ⇽---  ④
        ++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);        ⑤
}

如果y.load不在循环里,有可能会触发断言。

 

宽松次序(memory_order_relaxed)

采用宽松次序,name原子类型上的操作不存在同步关系,在单一线程内,同一个变量上的操作依然服从先行关系,但几乎不要求线程间存在任何次序关系。该内存次序的唯一要求是,在一个线程内,对相同变量的访问次序不得重新编排。对于给定的线程,一旦它见到某原子变量在某时刻持有的值,则该线程的后续读操作不可能读取相对更早的值。memory_order_relaxed次序无需任何额外的同步操作,线程间仅存的共有信息是每个变量的改动顺序。

 

例子:

#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);    ⇽---  ①
    y.store(true,std::memory_order_relaxed);    ⇽---  ②
}
void read_y_then_x()
{
    while(!y.load(std::memory_order_relaxed));    ⇽---  ③
    if(x.load(std::memory_order_relaxed))    ⇽---  ④
        ++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);    ⇽---  ⑤
}

 

因为在线程write_x_then_y()中,有可能执行顺序是先y.store(true),然后在线程等read_y_then_x执行完之后,再x.store(true)。所以断言可能为失败。

 

 

在读《深入理解linux内核》的时候,有了新的理解。所以再回头读一遍,这些接近底层的内容,还是要读底层的代码和相关书籍才能有更深的体会。

标签:std,load,read,C++,---,内存,memory,order
From: https://www.cnblogs.com/woodx/p/17378724.html

相关文章

  • 1 - Hand on system programming with Linux - 虚拟内存
    虚拟内存我的博客虚拟内存现代操作系统基于称作虚拟内存的内存模型。这些操作系统包括Linux、UNIX、MSWindows以及macOS。要想真正理解现代操作系统,必须深入理解虚拟内存以及内存管理。没有虚拟内存会有什么问题现在,让我们考虑一个只具有64MBRAM物理内存的情况。在老......
  • Windows亚克力特效代码实现(Dev c++可以编译通过)
    #include<windows.h>#include<dwmapi.h>//定义一个枚举类型,表示不同的窗口组合状态enumAccentState{ACCENT_DISABLED=0,ACCENT_ENABLE_GRADIENT=1,ACCENT_ENABLE_TRANSPARENTGRADIENT=2,ACCENT_ENABLE_BLURBEHIND=3,ACCENT_ENABLE_ACR......
  • 程序的内存空间
    进程的内存空间分布从下往上分别是:正文段(代码段).text只读,可共享;代码段(codesegment/textsegment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序......
  • c++结构体写入文档
    //#include<iostream>//#include<fstream>//usingnamespacestd;// structstudent// {// charname[20];// charsex[20];// intage;// };// intmain()// {// constintnum=2;// studentstu[num];// stringpath="1.txt";// fstreamf......
  • C++17 解构绑定
    在python中,加入我们有一个函数返回了两个数值,如:defgetData(x,y): returnx,y那么我们在使用这个函数时只需要使用两个新变量去接收函数返回值就可以:a,b=getData(4,5)但是对于C++来说就没有这么方便了,比如一个函数要返回多个数通常会把他们封装成一个pair或者vector容器......
  • 查看linux主机硬件配置:cpu /内存/显卡/磁盘
    CPU总览lscpu查看物理CPU个数cat/proc/cpuinfo|grep"physicalid"|sort|uniq|wc-l查看每个物理CPU中core的个数(即核数)cat/proc/cpuinfo|grep"cpucores"|uniq查看逻辑CPU的个数cat/proc/cpuinfo|grep"processor"|wc-l查看线程数grep'......
  • C/C++手机库存管理系统[2023-05-06]
    C/C++手机库存管理系统[2023-05-06]1、 设计并实习一个手机库存管理系统。要求实现至少以下功能:产品录入产品查询产品增加产品删除产品入库产品出库排序退出其中具体功能如下:产品录入:产品初始化。录入产品编号,产品品牌,产品型号,产品数量,产品价格。例如: 1001,华为,p30,10,5......
  • C/C++猜数字游戏[2023-05-06]
    C/C++猜数字游戏[2023-05-06]题目8:猜数字游戏软件题目描述设计一个猜数字游戏软件,该程序具有以下功能:(1)游戏区:玩家在此开始游戏,系统给出玩家每次猜数字后的提示,包括数值、位置均正确的个数,以及数值正确但位置不正确的个数。(2)刷新排行榜:当玩家成功完成一轮猜数字游戏后......
  • 【C++文件信息(空格信息)的读、写、追加、截断】
    文件数据类型:ofstream:创建文件+写入信息ifstream:读取信息fstream:创建文件+写入信息+读取信息文件打开模式:ios::app(追加)、ios::out(写)、ios::in(读)ios::ate(定位末尾)、ios::trunc(截断覆盖)打开文件:open(“文件名”,打开模式)关闭文件:close()写入文件:使用流插入运算......
  • C/C++活动管理程序[2023-05-06]
    C/C++活动管理程序[2023-05-06]设计一个活动管理程序,该程序具有以下功能:(1)多用管理,用户登录:输入用户名和密码,密码正确才允许登录。(2)可以创建活动,设定活动的内容,活动的人数,时间,要求等;(3)登录的所有用户可以查看当前所有正在征集的活动,并可以选择参加;(4)活动的创建者,可......