据我所知,在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-> 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
}

最佳答案

bDone没有同步。

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

解决此问题的正确方法是使用std::atomic<bool>。工作线程可以通过调用bDone.store(true)对其进行更新,而等待线程可以通过调用bDone.load()读取其最新值。

如果您想读入内存顺序以帮助理解为什么需要原子,则可以通过使用acquirerelease顺序来进一步改进此功能(尽管对于单元测试来说,这并不重要)。

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

10-06 01:43