我实现了一个简单的ThreadPool,它使用std::list<Tasks> mTasks
作为任务列表。
所有线程使用以下代码等待条件变量:
EnterCriticalSection(&mCriticalSection);
while(mTasks.size() ==0)
SleepConditionVariableCS(&mConditionVariable,&mCriticalSection, INFINITE);
直到有人在列表中添加内容然后其中一个被唤醒。
我用了一段时间来检查任务列表是不是空的,虽然被唤醒的唯一方法是向列表中添加一个新任务(因此它不能为空),我这样做的原因是因为在MSDN中写道:
条件变量受到虚假唤醒(那些不是 与明确的唤醒相关联和被盗的唤醒(另一个线程 设法在唤醒线程之前运行。因此,你应该重新检查 睡眠操作后的谓词(通常在while循环中) 回报。
但那些虚假的唤醒是什么,什么会唤醒我的变量?
答案 0 :(得分:2)
在我在大学学习的时候,我对这个主题的理解是,从绩效的角度来看,实施100%安全的条件变量实在太贵了。
关于spurious wakeups的维基百科页面引自David R. Butenhof(Programming with POSIX Threads
的作者)说:
这意味着当您等待条件变量时,等待可能会 (偶尔)没有专门广播的线程或 发出条件变量的信号。虚假的唤醒可能听起来很奇怪, 但在某些多处理器系统上,完全唤醒条件 可预测的可能会大大减慢所有条件变量 操作。引起虚假唤醒的竞争条件应该是 被认为是罕见的
检查while
循环中的条件是一种很好的做法,可以避免这个问题。
如果有更多关于为什么会发生这种情况的细节,我很抱歉,但我无法提供这样的见解。
答案 1 :(得分:1)
我认为问题在于多处理器系统中多个处理器之间的同步问题。
为了使条件变量保持尽可能轻量级,实现使用某些线程原语,虽然线程安全,但是当实际只有一个时,会导致条件变量注册两个notify()
样式调用。这是一种罕见的情况,而不是通过处理这种情况降低效率,设计人员将问题推送到用户代码中,如果它可能会影响您,您只需要担心它。