cpu cache的魔法

     首先简单了解一下什么是cpu cache这里推荐一篇文章吧,说的很详细。

     关于cpu cache:点击打开链接

     接下来我们看一些程序片段。

 

#include 
#include 
#include 

int flag = 1;
pthread_mutex_t lock;
void* my_thread(void *){
    sleep(1);
    printf("run thread1\n");
    
    pthread_mutex_lock(&lock);
    flag = -1;
    pthread_mutex_unlock(&lock);
    
    return NULL;
}

int main() {
    pthread_t t;
    int re = pthread_create(&t, NULL, &my_thread, NULL);
    if(re < 0){
        printf("create thread error\n");
        return 0;
    }
    
    pthread_mutex_lock(&lock);
    while(flag > 0){
//        sleep(1);
    }
    pthread_mutex_unlock(&lock);
    
    return 0;
}
#include 
#include 

int flag = 1;
pthread_mutex_t lock;
void* my_thread(void *){
    sleep(1);
    printf("run thread1\n");
    
    pthread_mutex_lock(&lock);
    flag = -1;
    pthread_mutex_unlock(&lock);
    
    return NULL;
}

int main() {
    pthread_t t;
    int re = pthread_create(&t, NULL, &my_thread, NULL);
    if(re < 0){
        printf("create thread error\n");
        return 0;
    }
    
    pthread_mutex_lock(&lock);
    while(flag > 0){
//        sleep(1);
    }
    pthread_mutex_unlock(&lock);
    
    return 0;
}

一眼看上去,程序没有任何问题,线程指之间共享的变量也加了锁,程序应该1s后打印run thread1,然后退出.

         gcc main.cpp -O  -pthread -g -o main执行以上命令编译,一定要加-O 开启编译器优化,如果不开启编译器优化则看不到效果,然后执行./main后发现程序并没有按我们想象的那样1s后退出,而是陷入了一个死循环,当我们给flag加上volatile关键字是程序就如预期的那样。

       volatile的特殊性在哪里,它是如何“防止”了线程不缓存共享变量的?常见的说法是使用volatile关键字之后,读取数据一定从内存中读取。实际上volatile关键字防止了编绎器优化,所以对于变量不会被放到寄存器里,或者被优化掉。但是volatile并不能防止CPU从Cache中读取数据。

      所谓的“缓存”到底是什么,CPU内部有寄存器,有各级Cache,L1,L2,L3。我们来考虑下到底怎样才会出现线程共享变量被放到CPU的寄存器或者各级Cache的情况,volatile阻止了编绎器把变量放到寄存器里,那么对线程共享变量的读取即直接的内存访问。

      CPU Cache放的正是内存的数据,而现代CPU有多核,通常来说每个核的L1, L2 Cache是不共享的,L3 Cache是共享的。那么问题就变成了:线程A修改了Cache中的内容,线程B是否会一直读取到的都是旧数据?既然Cache数据会不一致,那么自然要有个机制,让它们之间重回一致。经典的Cache一致性协议是MESI协议点击打开链接。

还有一个有趣的写法就是把注释打开,不用volatile程序也可正常运行,查了很多资料也没有结果,我的猜想是在调用sleep线程让出cpu使用权挂起,当cpu再次分配时间片给线程后,此时线程重新从内存中读取了flag的值,当然这只是我的猜想,很有可能是错的,希望哪位大神可以给我解惑。

       由此看来,我们在多线程开发中不仅仅是保证共享数据同一时刻只有一个线程访问那么简单,我们还要保证一个线程的改变对另一个线程的可见性。

       

    

 

 

  

     

 

  

你可能感兴趣的:(linux,c++学习)