信号和解锁订单

use*_*799 15 c++ pthreads

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();
}
Run Code Online (Sandbox Code Playgroud)

我按照教程,得到了这个.我想知道是否可以像这样更改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();
}
Run Code Online (Sandbox Code Playgroud)

如果我不能这样做,你能否详细说明为什么我不允许这样做?提前致谢.

bdo*_*lan 22

首先,这里没有正确性问题.任何订单都可以.回想一下,无论何时使用条件变量,都必须在等待时循环一个谓词:

pthread_mutex_lock(mutex);
while (!predicate)
  pthread_cond_wait(cvar);
pthread_mutex_unlock(mutex);
Run Code Online (Sandbox Code Playgroud)

通过解锁后发出信号,您不会引入任何正确性问题; 线程仍然保证会被唤醒,最糟糕的情况是另一个唤醒首先出现 - 此时它会看到谓词变为真,然后继续.

但是,可能会出现两个可能的性能问题.

  • "快点等一下".基本上,如果在保持锁定时发出信号,则另一个线程仍需要等到互斥锁可用.许多pthreads实现将而不是唤醒另一个线程,只需将其移动到互斥锁的等待队列,从而节省不必要的唤醒 - >等待周期.在某些情况下,这是未实现或不可用的,导致潜在的虚假上下文切换或IPI.
  • 虚假醒来.如果您在解锁后发出信号,则另一个线程可能会发出另一个唤醒.请考虑以下情形:

    1. 线程A开始等待将项添加到线程安全队列.
    2. 线程B在队列中插入一个项目.解锁队列后,但在发出信号之前,会发生上下文切换.
    3. 线程C在队列中插入一个项目,并发出cvar信号.
    4. 线程A唤醒并处理这两个项目.然后它回到等待队列.
    5. 线程B恢复,并发出信号.
    6. 线程A唤醒,然后立即返回睡眠状态,因为队列为空.

    如您所见,这可能会引入虚假唤醒,这可能会浪费一些CPU时间.

就个人而言,我认为无论如何都不值得担心.您不经常知道您的实现是否支持将服务器从条件变量移动到互斥等待队列,这是您可以用来决定使用哪个的唯一真正标准.

我的直觉是,如果我不得不选择,信号后解锁是轻微的效率低下,需要一个三线的比赛,而不是两线比赛中为"快点,等待不太可能推出的低效率, "条件.但是,这并不值得担心,除非基准测试显示过多的上下文切换开销.

  • 完美答案.这理解了所有要知道的事情. (2认同)