虚假唤醒后的互斥锁状态

Mutex status after spurious wakeup

考虑这个使用 pthreads 的基本多线程程序。 我们有一个主线程,创建另一个做一些工作的线程。

bool done = false;
mutex m; 
condition c;

void foo() {
    pthread_mutex_lock(&m);
    //while(!done) {
        pthread_cond_wait(&c, &m);
        // Spuriously wakeup while child is doing work.
        // child thread has NOT unlocked the mutex yet
        // Do I now own the mutex?
        // or am I waiting for child to unlock it?
    //}
    pthread_mutex_unlock(&m);
}

void * child(void *arg) {
    pthread_mutex_lock(&m);

    some_intense_work(); // this work is done while mutex is held!
    // the main thread spuriously wakes up
    // while this work is being done
    // (while this child thread is holding the mutex)

    done = true;
    pthread_cond_broadcast(&c);
    pthread_mutex_unlock(&m);
}

int main(int argc, char *argv[]) {
    pthread_t p;
    pthread_create(&p, NULL, child, NULL);
    foo();
}

假设我们实现了一个没有周围 while 子句检查谓词的等待,即使我们知道没有人应该这样做。

现在,如果在子线程工作时,主线程发生虚假唤醒,互斥体 m 的状态会是什么?主线程是否会在子线程不先解锁的情况下拥有它,以便双方都拥有它?

或者虚假唤醒只跳过对条件的等待,而不是等待释放互斥量?

pthread_cond_wait() 调用无法 'spuriously' 在其他线程持有关联的互斥体时唤醒。当 pthread_cond_wait() return 成功时,它将获得互斥量,因此在互斥量可用之前它无法成功 return。

在您的示例中,可能会发生虚假唤醒,因为 foo() 可以调用 pthread_cond_wait() 并在 child() 有机会调用 pthread_mutex_lock() 之前发生虚假唤醒首先。

您的示例中的另一个问题(禁用注释代码)是 pthread_cond_wait() 调用可能 never 唤醒。如果 child()foo() 设法获取互斥锁之前完成所有处理,就会发生这种情况。在这种情况下,child() 将在主线程等待 pthread_cond_wait() 之前调用 pthread_cond_broadcast(),因此主线程将错过广播。由于 foo() 在持有互斥量时从不检查 done,因此它不会注意到 child() 已完成其工作。

这就是为什么 pthread_cond_wait() 几乎总是必须在检查条件的循环中执行。