我已经查看了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
和{{1} }来电wait
。
进行一些汇编调试,__crtSleepConditionVariableSRW
只需提取__crtSleepConditionVariableSRW
函数指针,然后调用它。
事情就是这样:据我所知,win32 SleepConditionVariableSRW
不是内核对象,而是用户模式对象。因此,如果某个线程通知此变量并且没有线程实际上在其上休眠,则您丢失了通知,并且线程将保持休眠状态,直到超时或其他某个线程通知它。一个小程序实际上可以证明它 - 如果你错过了通知点 - 你的线程将保持睡眠,虽然其他一些线程通知它。
我的问题是这样的:
一个线程在条件变量上等待,谓词返回false。然后,发生上面解释的整个呼叫链。在那段时间,另一个线程改变了环境,因此谓词将返回true 和通知条件变量。我们在原始帖子中传递了谓词,但我们仍然没有进入CONDITION_VARIABLE
- 调用链很长。
所以,虽然我们通知了条件变量和,但是条件变量上的谓词肯定会返回true(因为通知符是这样的),我们仍然会阻塞条件变量,可能永远。
它应该如何表现?这似乎是一个巨大的丑陋竞争条件等待发生。如果您通知条件变量并且它的谓词返回true,则该线程应该解除阻塞。但是,如果我们在检查谓词和睡觉之间处于不确定状态 - 我们将永远被阻止。 SleepConditionVariableSRW
不是原子功能。
该标准对此有何规定,是否真的是竞争条件?
答案 0 :(得分:2)
你违反了合同,所以所有的赌注都没有了。请参阅:http://en.cppreference.com/w/cpp/thread/condition_variable
TLDR:当你持有互斥锁时,谓词不可能由别人改变。
您应该在持有互斥锁和的同时更改谓词的基础变量,您必须在调用std::condition_variable::wait
之前获取该互斥锁(因为wait
释放了互斥,因为这是合同)。
在您所描述的情景中,发生了 while (!_Pred())
后,wait(_Lck)
看到谓词不成立但在std::condition_variable::wait
有机会释放互斥锁之前。这意味着您更改了谓词检查的内容而不保留互斥锁。你违反了规则,竞争条件或无限等待仍然不是你能得到的最糟糕的UB。至少这些是本地的并且与您违反的规则相关,因此您可以找到错误...
如果你遵守规则,可以:
std::condition_variable::notify_one
。 (回想一下通知程序仍在等互斥锁上。)或:
std::condition_variable::notify_one
,但一旦释放互斥锁......)std::condition_variable::wait
。while (!_Pred())
和中提琴!谓词是真的。wait
,因此通知人是否设法调用std::condition_variable::notify_one
或者没有设法做到这一点是无关紧要的。这是cppreference.com要求背后的基本原理:
即使共享变量是原子的,也必须在互斥锁下对其进行修改,以便将修改正确地发布到等待的线程。
请注意,这是条件变量的一般规则,而不是std::condition_variables
的特殊要求(包括Windows CONDITION_VARIABLE
,POSIX pthread_cond_t
等。)
回想一下,带谓词的wait
重载只是一个便利函数,因此调用者不必处理虚假的唤醒。标准(§30.5.1/ 15)明确表示此重载等同于Microsoft实现中的while循环:
效果:相当于:
while (!pred()) wait(lock);
简单的wait
是否有效?你在调用wait
之前和之后测试谓词吗?大。你也是这样做的。或者您是否也在质疑void std::condition_variable::wait( std::unique_lock<std::mutex>& lock );
?
Windows Critical Sections和Slim Reader / Writer Lock是用户模式工具而不是内核对象,与问题无关,无关紧要。还有其他实施方案。如果您有兴趣知道Windows如何通过原子方式发布CS / SRWL并进入等待状态(使用Mutexes和Events的天真的Vista前用户模式实现错误),这是一个不同的问题。