2009-12-28 60 views
5

昨天關於雙重檢查鎖定的問題開始了一連串的想法,讓我無法確定一個簡單的情況。在下面的代碼中,是否有可能點擊「不再同步」的printf?在這個簡單的例子中,這些值可能會在同一個緩存行上,所以我認爲它不太可能(假設開始的可能性大於0%)。WaitForSingleObject是否充當內存屏障?

如果答案是「不,這是不可能的」,那麼我的後續問題是,相當可預測地:爲什麼不呢?直到昨天我的思緒糾結纏繞多線程軸,我認爲代碼纔是安全的。但是現在我想知道什麼可以防止從緩存中讀取papb之一的變量。如果pa, pb指向簡單的全局整數變量而不是malloc的內存,它會影響嗎? WaitForSingleObject調用是否提供內存屏障?或者應該將指針聲明爲volatile?這麼多問題,幾句話。

更新:我終於找到的信息,具體說,信號同步對象的功能使用memory barriers。它應該很明顯,但我無法找到明確的答案。所以我可以再次欺騙自己相信我理解這一切。

int i1 = 0; 
int i2 = 0; 
int reads = 0; 
int done = 0; 
int *pa = NULL; 
int *pb = NULL; 
HANDLE hSync = NULL; 

DWORD WriteThread(LPVOID pvParam) 
{ 
    while(!done) 
     { 
     WaitForSingleObject(hSync, INFINITE); 
     (*pa)++; 
     (*pb)++; 
     ReleaseSemaphore(hSync, 1, NULL); 
     } 
    return 0; 
} 

DWORD ReadThread(LPVOID pvParam) 
{ 
    while(!done) 
     { 
     WaitForSingleObject(hSync, INFINITE); 
     if (*pa != *pb) 
     { 
     printf("No longer in sync: %d, %d\n", *pa, *pb); 
     exit(1); 
     } 
     ReleaseSemaphore(hSync, 1, NULL); 
     reads++; 
     } 
    return 0; 
} 

int main(int argc, char* argv[]) 
{ 
    DWORD dwID; 

    // malloc'd memory 
    pa = (int*)malloc(sizeof(int)); 
    pb = (int*)malloc(sizeof(int)); 

    // Is a simple global variable different? 
    //pa = &i1; 
    //pb = &i2; 

    *pa = 0; 
    *pb = 0; 

    hSync = CreateSemaphore(NULL, 1, 1, NULL); 
    CreateThread(NULL, 0, WriteThread, NULL, 0, &dwID); 
    CreateThread(NULL, 0, ReadThread, NULL, 0, &dwID); 

    while (*pa < 1000000) 
     Sleep(1); 
    done = 1; 

    return 0; 
} 

回答

4

內存的位置並不重要,如果全部是關於高速緩存一致性,那麼聲明變量volatile將無能爲力。易失性的語義既不是線程安全所必需的也不足夠的;不要使用它!

在C/C++級別,pa和pb可以緩存在寄存器中,但在任何函數調用之後它們將被視爲過時。在CPU層面,所有等待功能都使用障礙來確保一切按預期工作。

+0

+1正是我想說的。 – tony 2009-12-28 07:05:41

+0

感謝您的信息。你碰巧知道一個鏈接,討論等待功能和內存障礙。這就是我一直在尋找,並沒有看到它。我很可能只是失明而錯過了一些明顯的東西。 – 2009-12-28 16:47:43

+2

你不是盲人;很難在網上找到相關信息。 MSDN在http://msdn.microsoft.com/en-us/library/ms686355%28VS.85%29.aspx上提供了相當好的概述。 – 2009-12-28 23:34:03