WaitForSingleObject sert-il de barrière de mémoire?

Hier, une question sur le locking à double contrôle a déclenché une série de reflections qui me laissaient incertain quant à une situation simple. Dans le code suivant, est-il possible de bash le printf de “N’est plus synchronisé”? Dans cet exemple simple, les valeurs seraient probablement sur la même ligne de cache, donc je pense que ce serait moins probable (en supposant que la possibilité est> 0% pour commencer).

Si la réponse est «Non, ce n’est pas possible». Alors, ma question suivante est plutôt prévisible: pourquoi pas? Jusqu’à ce que mes pensées soient emmêlées autour de l’axe multithreading, hier, je pensais que le code serait sécurisé. Mais maintenant je me demande ce qui empêche une lecture périmée du cache pour l’une des variables pa ou pb . Et serait-il important que pa, pb pointe vers de simples variables entières globales plutôt que de la mémoire malloc’d? L’appel WaitForSingleObject fournit-il une barrière de mémoire? Ou faut-il déclarer les pointeurs volatiles? Tant de questions, si peu de phrases.

Mise à jour : j’ai finalement localisé des informations indiquant spécifiquement que les fonctions qui signalent des objects de synchronisation utilisent des barrières de mémoire . Cela aurait dû être évident, mais j’avais du mal à trouver une réponse définitive. Ainsi, je peux encore une fois me leurrer en croyant tout comprendre.

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

Peu importe où se trouve la mémoire, et s’il ne s’agissait que de la cohérence du cache, déclarer les variables volatiles ne ferait rien pour y remédier. La sémantique de Volatile n’est ni nécessaire ni suffisante pour la sécurité des threads; ne l’utilisez pas!

Au niveau C / C ++, pa et pb peuvent être mis en cache dans des registres, mais ils seront considérés comme périmés après tout appel de fonction. Au niveau du processeur, toutes les fonctions d’attente utilisent des barrières pour s’assurer que tout fonctionne comme prévu.