关于缓存一致性的概念网上相关资料很多,这里不再赘述。本编文章主要通过C语言案例来更深一步理解缓存一致性原理。
理论
对于多线程进程来说 ,在有多个CPU或单个CPU多核的机器上执行时,肯定会保证数据多线程执行时的数据同步。如下图为单CPU双核结构。
首先我们得清楚几个概念:
ALU:算术逻辑单元(Arithmetic&logical Unit)专门做计算的。
Register:CPU寄存器,用于存储被计算的数据。
我们常说的CPU速度比内存速度快100倍,指的是CPU中ALU访问Register所花费的时间比访问内存所花费的时间快100倍。由于访问内存比较慢,因此有了三级缓存L1、L2、L3,具体位置如图所示,每个缓存里面都是由缓存行组成的,缓存系统中以缓存行(cache line)为单位存储的。缓存行大小是64字节。由于缓存行的特性,当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能。
案例:
下来我们通过一个案例来说明一下,代码如下:
/*************************************************** author : lijd date : 2022-08-18 ***************************************************/ #include <stdio.h> #include <stdio.h> #include <pthread.h> #define I_COUNT (1000000000) typedef struct myStr{ int x; // int a[16]; int y; }S_myStr; S_myStr s_t; //定义线程要执行的函数 void *Thread1() { int i = 0; for(i = 0; i < I_COUNT; i++) { s_t.x += 1; } return NULL; } //定义线程要执行的函数 void* Thread2() { int i = 0; for(i = 0; i < I_COUNT; i++) { s_t.y += 1; } return NULL; } int main() { int res; pthread_t mythread1, mythread2; /*创建线程 &mythread:要创建的线程 NULL:不修改新建线程的任何属性 ThreadFun:新建线程要执行的任务 NULL:不传递给 ThreadFun() 函数任何参数 返回值 res 为 0 表示线程创建成功,反之则创建失败。 */ res = pthread_create(&mythread1, NULL, Thread1, NULL); if (res != 0) { printf("线程创建失败"); return 0; } res = pthread_create(&mythread2, NULL, Thread2, NULL); if (res != 0) { printf("线程创建失败"); return 0; } res = pthread_join(mythread1, NULL); res = pthread_join(mythread2, NULL); return 0; }
进程执行如下:
结果分析:
两个线程分别操作全局变量中的结构体变量数据,由于缓存行的大小为64字节,所以第一种情况两个线程都会把结构体变量中的x, y缓存到自己的缓存行中,这样每次做计算时都需要线程同步,因此运行时长比较长。第二种情况x, y变量中间有int a[16]变量,超过64字节,因此两个线程的缓存行只存了自己所需的x或y,不涉及线程同步,因此用时较短。