天天看點

volatile 與記憶體屏障

編譯器和CPU執行過程中,可能會生成或者執行的機器碼,可能和我們編寫的代碼的預期的邏輯不一樣,可能會造成一些問題。

單線程中,這樣沒有問題,因為編譯器和CPU可以保證即使有調整,但是最後的結果是預期的。

但在多線程的場景下,可能會有問題,本文隻關注多線程修改和通路同一個公共變量的場景。

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

static volatile int vvv = 1;

void * thread1(void *n){
    sleep(2);
    printf("sss\n");
    vvv = -1;
    return NULL;
}
int main() {
    pthread_t t;
    int re = pthread_create(&t, NULL, &thread1, NULL);
    if(re < 0){
        perror("thread");
    }
   while(vvv > 0){
     __sync_synchronize ();
   }
    return 0;
}
           

如果沒有上面第6行或者第21行的代碼,并且編譯的時候用了-O3的優化選項,編譯器會将19 - 21的代碼優化成:

   while(1){
   }
           

是以可能導緻死循環。

volatile作用在編譯的時候,讓編譯器生成機器碼的時候,一方面,不做優化,老老實實按照代碼去翻譯,一方面,在運作的時候,被volatile修飾地變量不會被緩存到CPU的寄存器中,每次讀都是從記憶體中去讀。這樣可以解決上面代碼中的問題。

另外一種解決方式就是用形如__sync_synchronize(gcc和X86環境下) 這樣的函數,加一個邏輯上無實際意義的代碼,依照mem barrier的定義,此語句上面的語句會嚴格在下面語句之前執行,而且也讓編譯器的優化在此語句附近失效了,具體的細節需要檢視彙編代碼來檢視(TODO)。