WaitForSingleObject是否作为内存屏障?

7
昨天有一个关于双重检查锁定的问题引发了一连串的思考,让我对一个简单的情况感到不确定。在下面的代码中,“No longer in sync”的printf是否可能会被触发?在这个简单的例子中,这些值很可能在同一缓存行上,所以我认为这种情况不太可能发生(假设可能性大于0%)。
如果答案是“不可能”,那么我的后续问题就是:为什么呢?在昨天我把我的思维纠缠在多线程轴上之前,我认为这段代码是安全的。但现在我想知道是什么阻止了对于变量pa或pb的缓存中的旧数据读取。如果pa、pb指向的是简单的全局整数变量而不是malloc的内存,会有影响吗?WaitForSingleObject调用是否提供了内存屏障?或者应该将指针声明为volatile?有这么多问题,却只有这么少的句子。 更新:我最终找到了明确说明信号同步对象的功能使用内存屏障的信息。这应该是显而易见的,但我一直在努力寻找一个明确的答案。所以我又可以欺骗自己相信我理解了所有内容。
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;
}
1个回答

6

内存所在位置并不重要,如果仅涉及缓存一致性,则声明变量为volatile也无法解决问题。Volatile的语义既不必要也不足以保证线程安全,请勿使用!

在C/C++级别上,pa和pb可能缓存在寄存器中,但在任何函数调用后它们将被视为过时。在CPU级别上,所有等待函数都使用屏障来确保一切按预期工作。


谢谢您提供的信息。您是否知道有关等待函数和内存屏障的链接?这正是我正在寻找的,但没有找到。很可能是我眼瞎,错过了一些显而易见的东西。 - Mark Wilkins
4
你并不是盲目的;在网上找到相关信息确实很困难。MSDN在http://msdn.microsoft.com/en-us/library/ms686355%28VS.85%29.aspx提供了一个相当不错的概述。 - Marcelo Cantos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接