多线程的基础是优秀的内存模型
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
#include
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伪共享。