据我所知,在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
}
答案 0 :(得分:4)
bDone
没有同步。
当编译器的值为false时,编译器很可能将bDone
加载到寄存器中,然后继续使用寄存器缓存版本,而不是从内存中获取更新版本。或者,可以对您的说明进行重新排序,以便在释放锁定后将bDone
设置为false 。
正确的方法是使用std::atomic<bool>
。工作线程可以通过调用bDone.store(true)
来更新它,等待线程可以通过调用bDone.load()
将其读取为最新值。
如果你想阅读内存排序以帮助理解为什么需要原子,你可以使用acquire
和{进一步改善这一点(尽管对于单元测试,它确实无关紧要) {1}}订购。
除此之外,你真正应该做的是加入你的工作线程。在线程结束之前,连接将一直阻塞,因此可以确保您的release
函数已完成执行。如果您担心它可能会永远(或太长时间)运行,我建议您使用Compute
代替boost::thread
,因为它提供了std::thread
功能,在指定的时间段后停止等待线程。