【问题标题】:delete order/speed of std::lock_guard relative to other stack-allocated objects?删除 std::lock_guard 相对于其他堆栈分配对象的顺序/速度?
【发布时间】:2014-04-29 17:49:26
【问题描述】:

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

【问题讨论】:

  • 这段代码看起来很破。不能保证Compute 会在Reset 之前获得锁。此外,bDone 的访问也没有同步。享受你未定义的行为。
  • 对,这种情况最好用 std::future + std::async;出于本实验的目的,第一个 micro_wait(100) 会阻止 Reset 首先获得锁。我只是想解释一下在 Compute 之后为 bool 变量赋值的下一条语句如何在 Reset 完成之前无法完成。

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


【解决方案1】:

bDone 没有同步。

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

解决此问题的正确方法是使用std::atomic&lt;bool&gt;。工作线程可以通过调用bDone.store(true) 来更新它,而等待线程可以通过调用bDone.load() 来读取它的最新值。

如果您想读入内存排序以帮助理解为什么需要原子,您可以通过使用acquirerelease 排序进一步改进这一点(尽管对于单元测试,这真的无关紧要)。

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

【讨论】:

  • 是的,将 bool 替换为 atomic + 删除 worker.detach() + 在 EXPECT_TRUE(bDone) 导致良好行为之前插入 worker.join。
猜你喜欢
  • 2018-09-09
  • 1970-01-01
  • 2013-01-25
  • 2021-07-03
  • 1970-01-01
  • 2010-12-23
  • 1970-01-01
  • 2015-12-18
相关资源
最近更新 更多