3

关于这个: 如何使用条件变量

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

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()。

当主线程调用 pthread_cond_signal() 时,按照手册页,至少一个线程被唤醒。当它们中的任何一个从 pthread_cond_wait() 返回时,它会自动声明互斥锁。

所以我的问题是:关于提供的示例代码现在会发生什么?即,在互斥锁竞争中输掉的线程现在在做什么?

(AFAICT 赢得互斥锁的线程应该运行其余代码并释放互斥锁。丢失的线程应该卡在互斥锁上等待 - 在第一个嵌套 while循环中的某个地方 - 而获胜者持有它并在它被释放后开始阻塞 pthread_cond_wait() 因为到while (avail == 0)那时就会满足。我正确吗?)

4

4 回答 4

7

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

  • 它允许保证唤醒的一个线程消耗所有排队的工作单元
  • 它可以防止其他“意外”唤醒的线程假设有工作要做,而实际上可能没有工作,因为初始线程会处理所有这些工作。

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

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

所以pthread_cond_signal()调用时发生的事件顺序是:

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

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

希望这可以帮助。

于 2011-06-29T13:45:55.563 回答
1

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

于 2011-06-29T13:36:17.260 回答
0

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

啊,但事实并非如此。不是“自动”,也就是说,取决于“自动”的含义。您可能会对 ; 的“原子”语义感到困惑pthread_cond_wait。但是这种语义是在入口端播放的:在放弃互斥锁之前,以某种方式注册了一个线程等待条件,因此没有任何窗口在此期间线程不再拥有互斥锁,并且尚未等待在变量上。

从返回的每个线程pthread_cond_wait都必须获取互斥体并因此争夺它。那些在互斥锁竞争中失败的人必须阻塞互斥锁,就像他们调用pthread_mutex_lock.

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

没有被信号唤醒的线程“自动”获得互斥锁,在某种意义上,由于特殊资格而以某种方式转移了所有权。首先,在多处理器上,被唤醒的线程可能会失去与已经在另一个处理器上运行的线程的竞争,该线程抢夺互斥锁(如果可用),或者在接收信号的线程之前排队等待互斥锁。其次,调用的线程pthread_cond_signal可能本身并没有放弃互斥锁,并且可能会无限期地继续持有它,这意味着所有被唤醒的线程都会排队等待互斥锁操作,pthread_mutex_lock直到该线程放弃互斥锁才会出现。 .

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

于 2016-07-29T13:35:10.653 回答