2009-12-28 12 views
5

Una domanda di ieri sul blocco con doppio controllo ha dato il via a una catena di pensieri che mi ha lasciato insicuro su una situazione semplice. Nel seguente codice, è possibile premere printf di "Non più sincronizzato"? In questo semplice esempio, i valori sarebbero probabilmente sulla stessa riga della cache, quindi penso che sarebbe meno probabile (assumendo che la possibilità sia> 0% per cominciare).WaitForSingleObject funge da barriera di memoria?

Se la risposta è "No, non è possibile", quindi la mia domanda di follow-up è, piuttosto prevedibile: perché no? Fino a quando i miei pensieri si sono aggrovigliati e avvolti intorno all'assasse multithreading ieri, ho pensato che il codice sarebbe stato sicuro. Ma ora mi chiedo cosa impedisce una lettura stante dalla cache per una delle variabili pa o pb. E sarebbe importante se pa, pb indicasse semplici variabili globali intere piuttosto che memoria malloc'd? La chiamata WaitForSingleObject fornisce una barriera di memoria? O i puntatori dovrebbero essere dichiarati volatili? Così tante domande, così poche frasi.

Aggiornamento: ho finalmente individuato le informazioni che indicano in modo specifico che le funzioni che gestiscono gli oggetti di sincronizzazione utilizzano memory barriers. Doveva essere ovvio, ma stavo avendo problemi a trovare una risposta definitiva. Quindi posso ancora una volta illudermi nel credere di aver capito tutto.

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; 
} 

risposta

4

Non importa dove la memoria si trova, e se fosse tutto di coerenza della cache, poi dichiarando le variabili volatili non avrebbe fatto nulla per risolvere il problema. La semantica di Volatile non è né necessaria né sufficiente per la sicurezza del thread; non usarlo!

Al livello C/C++, pa e pb possono essere memorizzati nella cache nei registri, ma saranno considerati obsoleti dopo qualsiasi chiamata di funzione. A livello di CPU, tutte le funzioni di attesa utilizzano le barriere per assicurarsi che tutto funzioni come previsto.

+0

+1 esattamente quello che stavo per dire. – tony

+0

Grazie per l'informazione. Ti capita di conoscere un link che discute le funzioni di attesa e le barriere della memoria. Questo è quello che stavo cercando e non l'ho visto. È possibile che io sia solo cieco e mi sia sfuggito qualcosa di ovvio. –

+2

Non sei cieco; è difficile trovare informazioni pertinenti online. MSDN offre una panoramica ragionevolmente buona su http://msdn.microsoft.com/en-us/library/ms686355%28VS.85%29.aspx. –

Problemi correlati