删除std :: lock_guard相对于其他堆栈分配对象的顺序/速度?

时间:2014-03-22 06:43:37

标签: c++ multithreading c++11 locking mutex

据我所知,在lock_guard被删除和函数(在另一个线程中运行)实际返回之间有相当多的时间。请参阅TEST(...)

中的以下评论
bool bDone = false;
void  run_worker(Foo* f) {
  f->Compute();
  bDone = true;
}

TEST(FooTest,ThreadFoo) {
   Foo* f = makeFoo();
   std::thread  worker( run_worker, f );
   worker.detach();
   micro_wait(100); // wait for N microseconds

   f->Reset(); // should block until Compute() is done

   // !!?? Why is this necessary !?!? 
   int k=0;
   while(++k<500 && !bDone)
     micro_wait(100);**   

   EXPECT_TRUE(bDone); // Fails even with a single micro_wait(100)!     
}

对何时/为什么会有这样的时间流逝有一个很好的解释 在f-&gt; Compute()完成和bDone设置之间?我怀疑是互斥锁解锁了,而仍有工作要清理Compute()中分配的基于堆栈的变量,但这纯粹是一个假设。

计算和重置的存根如下:

void Foo::Compute() {
  std::lock_guard<std::mutex>  guard(m_Mutex);
  // ... allocate bunch of temporary stuff on stack, update *this
}

void Foo::Reset() {
  std::lock_guard<std::mutex>  guard(m_Mutex);
  // ... simpler stuff, clear
}

1 个答案:

答案 0 :(得分:4)

bDone没有同步。

当编译器的值为false时,编译器很可能将bDone加载到寄存器中,然后继续使用寄存器缓存版本,而不是从内存中获取更新版本。或者,可以对您的说明进行重新排序,以便在释放锁定后将bDone设置为false

正确的方法是使用std::atomic<bool>。工作线程可以通过调用bDone.store(true)来更新它,等待线程可以通过调用bDone.load()将其读取为最新值。

如果你想阅读内存排序以帮助理解为什么需要原子,你可以使用acquire和{进一步改善这一点(尽管对于单元测试,它确实无关紧要) {1}}订购。

除此之外,你真正应该做的是加入你的工作线程。在线程结束之前,连接将一直阻塞,因此可以确保您的release函数已完成执行。如果您担心它可能会永远(或太长时间)运行,我建议您使用Compute代替boost::thread,因为它提供了std::thread功能,在指定的时间段后停止等待线程。