void WorkHandler::addWork(Work* w){
printf("WorkHandler::insertWork Thread, insertWork locking \n");
lock();
printf("WorkHandler::insertWork Locked, and inserting into queue \n");
m_workQueue.push(w);
signal();
unLock();
}
我遵循了一个教程,我得到了这个。我想知道是否可以像这样更改 singal() 和 unLock() 的顺序
void WorkHandler::addWork(Work* w){
printf("WorkHandler::insertWork Thread, insertWork locking \n");
lock();
printf("WorkHandler::insertWork Locked, and inserting into queue \n");
m_workQueue.push(w);
unLock();
signal();
}
如果我不能这样做,您能否详细说明为什么我不能这样做? 提前致谢。
最佳答案
首先,这里不存在正确性问题。任何一个命令都行。回想一下,无论何时使用条件变量,都必须在等待时循环谓词:
pthread_mutex_lock(mutex);
while (!predicate)
pthread_cond_wait(cvar);
pthread_mutex_unlock(mutex);
通过在解锁后发出信号,您不会引入任何正确性问题;线程仍然可以保证唤醒,最坏的情况是另一个唤醒首先出现 - 此时它看到谓词变为真并继续。
但是,可能会出现两个性能问题。
- “快点等等”。基本上,如果您在持有锁时发出信号,则另一个线程仍然需要等待互斥锁可用。许多 pthreads 实现不会唤醒另一个线程,而是简单地将其移动到互斥锁的等待队列中,从而节省不必要的唤醒->等待周期。然而,在某些情况下,这未实现或不可用,从而导致潜在的虚假上下文切换或 IPI。
虚假唤醒。如果您在解锁后发出信号,则另一个线程可能会发出另一个唤醒。考虑以下场景:
- 线程 A 开始等待将项目添加到线程安全队列中。
- 线程 B 在队列中插入一个项目。在解锁队列之后,但在它发出信号之前,会发生上下文切换。
- 线程 C 在队列中插入一个项目,并发出 cvar 信号。
- 线程 A 被唤醒,并处理这两个项目。然后它又回到队列中等待。
- 线程 B 恢复,并向 cvar 发出信号。
- 线程 A 被唤醒,然后立即进入休眠状态,因为队列为空。
如您所见,这可能会引入虚假唤醒,这可能会浪费一些 CPU 时间。
就我个人而言,无论哪种方式,我都不值得为此担心太多。您通常不会立即知道您的实现是否支持将等待者从条件变量移动到互斥等待队列,这是您可以用来决定使用哪个真正的标准。
我的直觉是,如果我必须选择,在解锁后发出信号几乎不太可能引入低效率,因为低效率需要三线程竞赛,而不是两个-线程竞争“快点等待”条件。但是,这并不值得担心,除非基准测试显示过多的上下文切换开销或其他问题。
关于c++ - 信号和解锁命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6419117/