简体   繁体   English

WaitForSingleObject是否可用作内存屏障?

[英]Does WaitForSingleObject Serve as a Memory Barrier?

A question yesterday about doubled-checked locking started a chain of thoughts that left me unsure about a simple situation. 昨天关于双重检查锁定的一个问题引发了一连串的想法,让我不确定一个简单的情况。 In the following code, is it possible to hit the printf of “No longer in sync”? 在下面的代码中,是否可以点击“不再同步”的printf In this simple example, the values would likely be on the same cache line, so I think it would be less likely (assuming the possibility is > 0% to begin with). 在这个简单的例子中,值可能在同一个缓存行上,所以我认为它不太可能(假设开始时可能性> 0%)。

If the answer is, “No, it is not possible.”, then my follow-up question is, rather predictably: why not? 如果答案是“不,这是不可能的。”,那么我的后续问题是,相当可预见的:为什么不呢? Until I got my thoughts tangled and wrapped around the multi-threading axel yesterday, I assumed that the code would be safe. 直到昨天我的想法纠结并缠绕在多线程轴上,我认为代码是安全的。 But now I am wondering what prevents a stale read from the cache for one of the variables pa or pb . 但是现在我想知道是什么阻止了从缓存中读取一个变量papb的过时。 And would it matter if pa, pb pointed to simple global integer variables rather than malloc'd memory? 如果pa, pb指向简单的全局整数变量而不是malloc内存pa, pb那会不会有问题? Does the WaitForSingleObject call provide a memory barrier? WaitForSingleObject调用是否提供内存屏障? Or should the pointers be declared volatile? 或者指针是否应声明为volatile? So many questions, so few sentences. 这么多问题,句子很少。

Update : I finally located information that does specifically say that functions that signal synchronization objects do use memory barriers . 更新 :我最终找到的信息明确指出,信号同步对象的函数确实使用了内存屏障 It should have been obvious, but I was having trouble finding a definitive answer. 应该是显而易见的,但我找不到明确的答案。 So I can once again delude myself into believing I understand it all. 所以我可以再次欺骗自己相信我理解这一切。

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

It doesn't matter where the memory lies, and if it were all about cache coherency, then declaring the variables volatile would do nothing to fix it. 内存所在的位置并不重要,如果它都是关于缓存一致性的,那么声明变量volatile将无法解决它。 Volatile's semantics are neither necessary nor sufficient for thread-safety; 易失性的语义对于线程安全既不必要也不充分; don't use it! 不要用它!

At the C/C++ level, pa and pb may be cached in registers, but they will be considered stale after any function call. 在C / C ++级别,pa和pb可以缓存在寄存器中,但在任何函数调用之后它们将被视为过时。 At the CPU level, all wait functions use barriers to make sure everything works as expected. 在CPU级别,所有等待函数都使用障碍来确保一切按预期工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM