2009-12-28 11 views
5

Eine Frage gestern über doppelt geprüftes Sperren begann eine Gedankenkette, die mich über eine einfache Situation unsicher machte. Im folgenden Code ist es möglich, die printf von "Nicht mehr synchron" zu treffen? In diesem einfachen Beispiel würden sich die Werte wahrscheinlich auf der gleichen Cache-Zeile befinden, daher denke ich, dass dies weniger wahrscheinlich ist (unter der Annahme, dass die Wahrscheinlichkeit> 0% beträgt).Funktioniert WaitForSingleObject als Speicherbarriere?

Wenn die Antwort lautet: "Nein, das ist nicht möglich.", Dann ist meine Folgefrage eher vorhersehbar: warum nicht? Bis ich meine Gedanken gestern um die Multi-Threading-Achse gewickelt habe, nahm ich an, dass der Code sicher wäre. Aber jetzt frage ich mich, was ein veraltetes Lesen aus dem Cache für eine der Variablen pa oder pb verhindert. Und wäre es wichtig, wenn pa, pb auf einfache globale Integer-Variablen und nicht auf Malloc-Speicher verweist? Bietet der WaitForSingleObject-Aufruf eine Speicherbarriere? Oder sollten die Zeiger für flüchtig erklärt werden? So viele Fragen, so wenige Sätze.

Update: Ich fand schließlich Informationen, die ausdrücklich sagen, dass Funktionen, die Signal-Synchronisation Objekte verwenden memory barriers. Es hätte offensichtlich sein müssen, aber ich hatte Probleme, eine definitive Antwort zu finden. So kann ich mich selbst wieder dazu verleiten zu glauben, dass ich alles verstehe.

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

Antwort

4

Es spielt keine Rolle, wo der Speicher liegt, und wenn es nur um die Cache-Kohärenz ist, dann die Variablen flüchtig erklärt nichts, es zu beheben tun würde. Volatiles Semantik ist weder notwendig noch ausreichend für die Fadensicherheit; benutze es nicht!

Auf der C/C++ - Ebene können pa und pb in Registern zwischengespeichert werden, sie werden jedoch nach jedem Funktionsaufruf als veraltet betrachtet. Auf CPU-Ebene verwenden alle Wartefunktionen Barrieren, um sicherzustellen, dass alles wie erwartet funktioniert.

+0

+1 genau das, was ich sagen wollte. – tony

+0

Vielen Dank für die Informationen. Kennen Sie zufällig einen Link, der die Wartefunktionen und Speicherbarrieren diskutiert? Das habe ich gesucht und nicht gesehen. Es ist durchaus möglich, dass ich nur blind bin und etwas Offensichtliches verpasst habe. –

+2

Sie sind nicht blind; Es ist schwierig, relevante Informationen online zu finden. MSDN bietet einen relativ guten Überblick unter http://msdn.microsoft.com/en-us/library/ms686355%28VS.85%29.aspx. –

Verwandte Themen