C++ 20 内存模型(一)
多线程的基础是优秀的内存模型
C++20 内存模型:
- 高复杂性, 难以理解
- 对多线程有更深入的理解
内存布局
位域
member_name
成员名, width
bit 宽度
struct bit_field_name
{type member_name : width;
};
struct my_struct
{char a;int b : 5; // 最大存储 2^5 ,只使用低五位节约空间...int c : 11,: 0,d : 8;int e;double f;string g;
};
位域的对齐
不允许跨越 uint 大小的区域
struct my_struct2
{char a;int c : 30;int b : 5;
};
c
: 30 bit + 空2 bit + b
5 bit
例子:
#include <iostream>
#include <thread>
using namespace std;struct my_struct
{char a;int b : 5;int c : 11,: 0,d : 8;int e;double f;string g;
};int main(int argc, char* argv[])
{my_struct s{.a = 'a',.b = 1,.c = 2,.d = 3,.e = 4,.f = 5.0,.g = "hello"};cout << &s << endl;cout << "sizeof(my_struct) = " << sizeof(my_struct) << endl;
}
使用内存查看工具查看内存
**这种方式可以解决内存伪共享问题: **
来举一个java多线程的例子
class MyObject
{
private long a;
private long b;
private long c;
}
按照Java规范,MyObject
的对象是在堆内存上分配空间存储的,而且a、b、c三个属性在内存空间上是近邻,如下所示。
a(8个字节) b(8个字节) c(8个字节)
我们知道,X86的CPU中Cache Line的长度为64字节,这也就意味着MyObject
的3个属性(长度之和为24字节)是完全可能加载在一个Cache Line里的。如此一来,如果我们有两个不同的线程(分别运行在两个CPU上)分别同时独立修改a与b这两个属性,那么这两个CPU上的Cache Line可能出现如下所示的情况,即a与b这两个变量被放入同一个Cache Line里,并且被两个不同的CPU共享。
根据MESI协议的相关知识,我们知道,如果Thread 0要对a变量进行修改,则因为CPU 1上有对应的Cache Line,这会导致CPU 1的Cache Line无效,从而使得Thread 1被迫重新从Memory里获取b的内容(b并没有被其他CPU改变,这样做是因为b与a在一个Cache Line里)。同样,如果Thread 1要对b变量进行修改,则同样导致Thread 0的Cache Line失效,不得不重新从Memory里加载a。如此一来,本来是逻辑上无关的两个线程,完全可以在两个不同的CPU上同时执行,但阴差阳错地共享了同一个Cache Line并相互抢占资源,导致并行成为串行,大大降低了系统的并发性,这就是所谓的Cache伪共享。