【发布时间】:2017-07-17 04:22:30
【问题描述】:
我研究了std::condition_variable(lock,pred)的VC++实现,基本上是这样的:
template<class _Predicate>
void wait(unique_lock<mutex>& _Lck, _Predicate _Pred)
{ // wait for signal and test predicate
while (!_Pred())
wait(_Lck);
}
基本上,裸wait 调用_Cnd_waitX 调用_Cnd_wait 调用do_wait 调用cond->_get_cv()->wait(cs);(所有这些都在文件cond.c 中)。
cond->_get_cv() 返回Concurrency::details::stl_condition_variable_interface。
如果我们转到文件primitives.h,我们会看到在windows 7 及更高版本下,我们有类stl_condition_variable_win7,其中包含旧好的win32 CONDITION_VARIABLE,而wait 调用__crtSleepConditionVariableSRW。
做一点汇编调试,__crtSleepConditionVariableSRW 只需提取SleepConditionVariableSRW 函数指针,然后调用它。
事情是这样的:据我所知,win32 CONDITION_VARIABLE 不是内核对象,而是用户模式对象。因此,如果某个线程通知了这个变量,而实际上没有线程在它上面休眠,则您丢失了通知,并且线程将保持休眠状态,直到超时或其他线程通知它。一个小程序实际上可以证明这一点——如果你错过了通知点——你的线程将保持休眠状态,尽管其他线程通知了它。
我的问题是这样的:
一个线程等待条件变量,谓词返回 false。然后,发生上面解释的整个调用链。在那个时候,另一个线程改变了环境,所以谓词将返回 true 并且通知条件变量。我们在原始线程中传递了谓词,但我们仍然没有进入SleepConditionVariableSRW - 调用链很长。
所以,虽然我们通知了条件变量并且放在条件变量上的谓词肯定会返回真(因为通知器这样做了),但我们仍然阻塞在条件变量上,可能会永远阻塞。
这是它的行为方式吗?这似乎是一个巨大的丑陋的比赛条件等待发生。如果您通知条件变量并且它的谓词返回 true - 线程应该解除阻塞。但是,如果我们在检查谓词和睡觉之间处于两难境地——我们将永远被阻塞。 std::condition_variable::wait 不是原子函数。
标准对此有何规定?它真的是一种竞争条件吗?
【问题讨论】:
标签: c++ multithreading synchronization condition-variable