C++ 信号和解锁命令
我遵循了一个教程,我得到了这个。我想知道这样更改singal()和unLock()的顺序是否可以C++ 信号和解锁命令,c++,pthreads,C++,Pthreads,我遵循了一个教程,我得到了这个。我想知道这样更改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_wor
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();
}
如果我不能这样做,你能告诉我为什么我不能这样做的细节吗?
提前谢谢。您的问题的答案是“是”。事实上,这稍微好一点(你可能已经猜到了),因为它避免了“匆忙等待”的问题,即唤醒一个线程来测试一个条件,而只是让它在测试条件之前立即阻塞它需要获取的互斥体 这个答案是基于以下猜测得出的:
是lock
的薄包装pthread\u mutex\u lock
是unLock
的薄包装pthread\u mutex\u unLock
是signal
的东西包装器pthread\u cond\u signal
- 锁定和解锁的互斥锁是您给
的互斥锁pthread\u cond\u wait
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();
}
通过在解锁后发送信号,您不会引入任何正确性问题;线程仍然保证会被唤醒,最糟糕的情况是另一个唤醒首先出现——此时它看到谓词变为true并继续
但是,可能会出现两个性能问题
- “快点等等”。基本上,如果您在保持锁的同时发出信号,那么另一个线程仍然需要等待,直到互斥锁可用。许多pthreads实现将不唤醒另一个线程,而只是将其移动到互斥体的等待队列,从而节省不必要的唤醒->等待周期。然而,在某些情况下,这是未实现或不可用的,导致潜在的虚假上下文切换或IPI
- 虚假的唤醒。如果在解锁后发出信号,另一个线程可能会发出另一个唤醒。考虑下面的场景:
- 线程A开始等待将项添加到线程安全队列
- 线程B在队列中插入一个项目。解锁队列后,但在发出信号之前,会发生上下文切换
- 线程C在队列中插入一个项目,并发出cvar信号
- 线程A唤醒,并处理这两个项目。然后它又回到排队等候
- 线程B恢复,并向cvar发送信号
- 线程A被唤醒,然后立即返回睡眠状态,因为队列是空的 如您所见,这可能会引入虚假唤醒,这可能会浪费一些CPU时间
我的直觉是,如果我必须选择的话,解锁后的信号发送不太可能导致效率低下,因为效率低下需要三个线程的竞争,而不是两个线程的“快速等待”状态。然而,这并不值得担心,除非基准测试显示了太多的上下文切换开销或其他问题。这篇文章确实值得一读,以回答您的问题: 假设您将相同的互斥体与条件变量一起使用,以使条件更改成为原子性的。有两种情况,您应该了解他们的行为:
对于解锁互斥锁之前的信令,它可能会导致虚假唤醒。如果您的代码设计得不好,无法处理虚假唤醒所做的谓词更改,则应在保持锁定的同时选择signal。pthread_cond_signal的手册页上说,您可以发出信号,表明您是否拥有互斥锁(基本上是在锁定和解锁之间的任何时间)可能存在的not true副本。等待线程在获取互斥锁之前无法继续,而在“信号器”解锁互斥锁之前,互斥锁无法发生。原来的顺序实际上对性能更好。(有关详细信息,请参见“重复”问题中的已接受答案。)您当然可以“袖手旁观”您的答案。但是,如果你打算推荐一个与世界上所有其他参考文献基于“性能”而推荐的序列相反的序列,我认为你有责任提供性能分析。所以我支持我的反对票。@Nemo,在某些情况下,将服务员转移到互斥对象的等待队列是不可能的。在Linux上,当condvar设置了其
pshared
属性时就是这种情况,例如,在发送condvar信号的过程中,互斥锁的地址可能不同或完全不可用。事实上,这是不使用pshared默认值的动机之一(另一个原因是mm private futex哈希表减少了哈希冲突的发生)@Nemo,是的,如果您知道pthreads实现支持移动waiter,那么这种方式可能会稍微好一些。另一方面,如果y,则解锁后的信号发送更好(可能幅度更大)
pthread_mutex_lock(mutex);
while (!predicate)
pthread_cond_wait(cvar);
pthread_mutex_unlock(mutex);