Dient WaitForSingleObject als Speicherbarriere?

Dient WaitForSingleObject als Speicherbarriere?


Eine gestrige Frage zum doppelt geprüften Sperren startete eine Gedankenkette, die mich in einer einfachen Situation unsicher machte. Im folgenden Code ist es möglich, printf zu treffen von „Nicht mehr synchron“? In diesem einfachen Beispiel würden sich die Werte wahrscheinlich in derselben Cache-Zeile befinden, daher denke ich, dass dies weniger wahrscheinlich wäre (vorausgesetzt, die Wahrscheinlichkeit ist zunächst> 0 %).


Wenn die Antwort lautet:„Nein, das ist nicht möglich“, lautet meine Folgefrage ziemlich vorhersehbar:Warum nicht? Bis ich gestern meine Gedanken verwirrt und um die Multi-Threading-Axel gewickelt habe, bin ich davon ausgegangen, dass der Code sicher sein würde. Aber jetzt frage ich mich, was ein veraltetes Lesen aus dem Cache für eine der Variablen pa verhindert oder pb . Und wäre es wichtig, wenn pa, pb auf einfache globale Integer-Variablen verwiesen und nicht auf Speicher? Stellt der WaitForSingleObject-Aufruf eine Speicherbarriere bereit? Oder sollten die Zeiger als flüchtig deklariert werden? So viele Fragen, so wenige Sätze.


Aktualisieren :Ich habe endlich Informationen gefunden, die ausdrücklich besagen, dass Funktionen, die Synchronisationsobjekte signalisieren, Speicherbarrieren verwenden. Es hätte offensichtlich sein müssen, aber ich hatte Probleme, eine endgültige Antwort zu finden. So kann ich mich wieder einmal der Illusion hingeben, 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;
}

Antworten:


Es spielt keine Rolle, wo sich der Speicher befindet, und wenn es nur um Cache-Kohärenz ginge, würde das Deklarieren der Variablen als flüchtig nichts tun, um das Problem zu beheben. Die Semantik von Volatile ist für die Thread-Sicherheit weder notwendig noch ausreichend; nicht verwenden!


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