为什么pthread_cond_signal会导致死锁?

4

我对条件变量不太熟悉,如果不使用pthread_cond_broadcast()会出现死锁。

#include <iostream>
#include <pthread.h>

pthread_mutex_t m_mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;

bool ready = false;

void* print_id (void *ptr )
{
    pthread_mutex_lock(&m_mut);
    while (!ready) pthread_cond_wait(&cv, &m_mut);
    int id = *((int*) ptr);
    std::cout << "thread " << id  << '\n';
    pthread_mutex_unlock(&m_mut);
    pthread_exit(0);
    return NULL;
}

这里的条件已经发生了改变!

void go() {
    pthread_mutex_lock(&m_mut);
    ready = true;
    pthread_mutex_unlock(&m_mut);
    pthread_cond_signal(&cv);
}

如果我将go()的最后一行改为pthread_cond_broadcast(&cv);,它就可以工作。
int main ()
{
    pthread_t threads[10];

    // spawn 10 threads:
    for (int i=0; i<10; i++)
        pthread_create(&threads[i], NULL, print_id, (void *) new int(i));

    go();

    for (int i=0; i<10; i++) pthread_join(threads[i], NULL);

    pthread_mutex_destroy(&m_mut);
    pthread_cond_destroy(&cv);

    return 0;
}

期望的答案(顺序任意)是:
thread 0

....

thread 9

然而,在我的电脑上(Ubuntu),它没有输出任何内容。有人能告诉我原因吗?谢谢。

是的。_signal会唤醒等待列表中的任意线程。我有遗漏吗? - luyi0619
然而...它什么也没打印。真的吗?乍一看我有些怀疑。假设stdout是行缓冲的(例如,终端),我理解程序可能会打印从1到10行输出的原因,但不会没有任何输出。如果某些线程比“main”更快,那么它们中的至少一个将被通知并产生输出。如果某些线程比“main”慢,它们会发现“ready”已经准备好并且每个线程都会输出一行。 - pilcrow
1个回答

1

从手册页面(我强调):

pthread_cond_signal 重新启动等待在条件变量 cond 上的线程之一。如果没有线程在等待 cond,则不会发生任何事情。如果有多个线程在等待 cond,则只会重新启动一个线程,但未指定是哪个。

pthread_cond_broadcast 重新启动等待在条件变量 cond 上的所有线程。如果没有线程在等待 cond,则不会发生任何事情。

您的十个线程中的每一个都在等待相同的条件。您仅调用一次 go() - 这是从 main() 中调用的。这将调用 pthread_cond_signal,它只会向其中一个线程(任意一个)发出信号。其他所有线程仍将等待,因此 pthread_join 将挂起,因为它们不会终止。当您将其切换到 pthread_cond_broadcast 时,所有线程都会被触发。


在我的代码中,一个线程必须在等待条件变量之前获取互斥锁。我认为只有一个线程可以等待cv,我是对的吗? - luyi0619
是的。pthread_cond_wait 要求在进入时互斥锁被锁定,并自动解锁它,在退出时重新锁定它。 - abligh
嗯,我仍然不明白为什么它没有打印任何东西。这十个线程是否在cv上等待?如果是的话,为什么我不能使用_signal? - luyi0619
我不能告诉你所有十个线程是否都在等待cv,因为你没有展示所有的代码,但我只能假设这就是正在发生的事情。如果它们都调用了print_id,那么是的,它们会这样做。如果是这样,pthread_cond_signal不会起作用,因为它将随机触发其中一个等待的线程,并且你只调用了一次go(),所以只有一个会被触发。 - abligh
实际上,是的,你已经展示了足够的代码来证明这绝对是问题所在。我会修改我的答案。 - abligh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接