由pthread_cond_signal()唤醒但失去了对互斥锁的竞争的线程会发生什么

时间:2011-06-29 12:45:03

标签: c pthreads mutex condition-variable

关于这个: How To Use Condition Variable

假设我们有许多执行此类代码的消费者线程(从引用的页面复制):

while (TRUE) {
    s = pthread_mutex_lock(&mtx);
    while (avail == 0) {   /* Wait for something to consume */
       s = pthread_cond_wait(&cond, &mtx);
    }
    while (avail > 0) {   /* Consume all available units */ 
        avail--;
    }
    s = pthread_mutex_unlock(&mtx);
}

我假设这里的场景是:主线程调用pthread_cond_signal()来告诉消费者线程做一些工作。

据我所知 - 后续线程调用pthread_mutex_lock()然后调用pthread_cond_wait()(以原子方式解锁互斥锁)。到目前为止,没有任何消费者线程声称使用互斥锁,它们都在pthread_cond_wait()上等待。

当主线程在manpage之后调用pthread_cond_signal()时,至少会唤醒一个线程。当其中任何一个从pthread_cond_wait()返回时,它会自动声明互斥锁。

所以我的问题是:现在关于提供的示例代码会发生什么? 即,失去互斥体竞赛的线程现在做了什么?

(AFAICT赢得互斥锁的线程,应该运行其余的代码并释放互斥锁。丢失的那个应该等待互斥锁 - 在第一个嵌套的某个地方 {{ 1}}循环 - 当胜利者持有它并且在它被释放之后开始阻止pthread_cond_wait()因为那时while将被满足。我是否正确?)

4 个答案:

答案 0 :(得分:6)

请注意,pthread_cond_signal()通常只用于唤醒一个等待线程(这就是它保证的全部)。但它可能会“意外地”唤醒。 while (avail > 0)循环执行两个函数:

  • 它允许一个线程保证被唤醒以消耗所有排队的工作单元
  • 它可以防止额外的'意外'唤醒线程假设有工作要做,当可能没有,因为初始线程会处理所有这些。

它还可以防止在while (avail > 0)完成之后但在工作线程再次等待条件之前工作单元可能已放入队列的竞争条件 - 但该竞争也由在调用if之前进行pthread_cond_wait()测试。

基本上当一个线程被唤醒时,它只知道可能是它要使用的工作单元,但可能没有(另一个线程可能已经消耗它们)。

因此调用pthread_cond_signal()时发生的事件序列为:

  • 系统将唤醒一个或多个等待条件的线程
  • 所有被唤醒的线程将尝试获取互斥锁 - 只有其中一个可以在任何特定时刻获取它,因为这是互斥的目的
  • 该线程将继续,在while (avail > 0)循环中执行工作,然后将释放互斥锁
  • 此时,之前被唤醒的其他线程之一将获取互斥锁并运行相同的循环,然后释放互斥锁。通常,将不再有可用的工作单元(因为第一个线程将消耗所有工作单元),但如果另一个线程添加了一个额外的单元(或更多),那么该线程将处理该工作
  • 下一个线程将获取互斥锁并执行同一组逻辑

答案 1 :(得分:2)

pthread_cond_wait()必须在发出信号/唤醒后获得给定的互斥量。如果另一个线程赢得该竞争,该函数将阻塞,直到释放互斥锁。因此从应用程序的角度来看,它不会返回,直到当前线程持有互斥锁。等待总是在循环中完成(上面while (avail == 0) { ...)以确保我们正在等待的应用程序条件仍然保持(缓冲区不为空,更多可用工作等)

希望这有帮助。

答案 2 :(得分:1)

一旦互斥锁解锁,丢失比赛的线程就会唤醒,再次检查条件,然后在条件变量上进入睡眠状态。

答案 3 :(得分:0)

  

当其中任何一个从pthread_cond_wait()返回时,它会自动声明互斥锁。

啊,但事实并非如此。不是“自动”,也就是说,取决于“自动”的含义。您可能会对$(this).closest('div.theaterPlayer').remove(); //Removes all given(html) Div's and show length as 0; 的“原子”语义感到困惑;但是这个语义在入口端播放:一个线程以某种方式注册,在放弃互斥锁之前等待条件,因此没有任何窗口,在此期间线程不再具有互斥锁,并且还没有等待关于变量。

pthread_cond_wait返回的每个线程都必须获取互斥锁并因此争用它。那些失去互斥体竞争的人必须阻止互斥锁,就好像他们叫pthread_cond_wait一样。

pthread_mutex_lock退出时获取互斥锁的方式可以建模为常规pthread_cond_wait操作。本质上,线程必须在互斥锁上排队才能退出。获取互斥锁的每个线程然后从该函数返回;其他人必须等到该线程在允许返回之前放弃互斥锁。

信号唤醒的线程没有“自动”获取互斥锁,在某种意义上由于特殊资格而转移所有权。首先,在多处理器上,唤醒线程可能会丢失已经在另一个处理器上运行的线程的竞争,该处理器抢占互斥锁(如果可用),或者排队等待接收信号的线程之前的互斥锁。其次,调用pthread_mutex_lock的线程本身可能没有放弃互斥锁,并且可能会无限期地继续保持它,这意味着所有唤醒线程将在互斥锁操作上排队,并且不会从{{{{{ 1}}直到该线程放弃互斥锁。

所有“自动”是pthread_cond_signal操作在再次获取互斥锁之前不会返回,因此应用程序不必采取步骤来获取互斥锁。