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();
}
如果我不能这样做,请你详细说明为什么我不允许这样做? 提前谢谢。
答案 0 :(得分:22)
首先,这里没有正确性问题。任何订单都可以。回想一下,无论何时使用条件变量,都必须在等待时循环一个谓词:
pthread_mutex_lock(mutex);
while (!predicate)
pthread_cond_wait(cvar);
pthread_mutex_unlock(mutex);
通过解锁后发出信号,您不会引入任何正确性问题;线程仍然可以保证唤醒,最糟糕的情况是第一次唤醒 - 此时它会看到谓词变为真,然后继续。
但是,可能会出现两个可能出现的性能问题。
虚假的唤醒。如果您在解锁后发出信号,则另一个线程可能会发出另一个唤醒。请考虑以下情形:
如您所见,这可能会引入虚假的唤醒,这可能会浪费一些CPU时间。
就个人而言,我认为无论如何都不值得担心。您不经常知道您的实现是否支持将服务器从条件变量移动到互斥等待队列,这是您可以用来决定使用哪个的真正标准。
我的直觉是,如果我必须选择,解锁后发出的信号略微不太可能引入低效率,因为效率低下需要三线竞赛,而不是两个 - “快点等待”条件的线程竞赛。但是,这并不值得担心,除非基准测试显示过多的上下文切换开销等。
答案 1 :(得分:2)
您的问题的答案是“是”。事实上,它稍微好一点(正如你可能已经猜到的那样),因为它避免了“快点等待”的问题,即唤醒线程测试条件只是为了让它立即阻塞它需要在测试之前获取的互斥锁条件。
这个答案取决于这些事情的猜测:
lock
是pthread_mutex_lock
的精简包装。unLock
是pthread_mutex_unlock
的精简包装。signal
是pthread_cond_signal
的包装器。pthread_cond_wait
提供的互锁锁。答案 2 :(得分:1)
这篇文章非常值得您阅读:
假设您使用与条件变量相同的互斥锁将条件更改为原子。有两种情况你应该知道他们的行为:
Pthreads通过 wait-morphing 实现,也就是说,它不是在发出信号时唤醒线程,而只是将条件变量上的线程传递给附加的互斥队列。因此,在没有太多性能影响的情况下,锁定信号更为可取。
对于解锁互斥锁之前的信令,可能会导致虚假唤醒。如果您的代码没有很好地设计来处理虚假唤醒所做的谓词更改,那么您应该在按住锁定时选择信号。