pthread_cond_wait() 同时唤醒两个线程

Posted

技术标签:

【中文标题】pthread_cond_wait() 同时唤醒两个线程【英文标题】:pthread_cond_wait() waking up two threads at the same time 【发布时间】:2015-07-15 21:04:42 【问题描述】:

我试图更好地了解如何使用pthread_cond_wait() 以及它是如何工作的。 我只是想对我在这个网站上看到的答案进行一些澄清。

答案是本页最后的回复

understanding of pthread_cond_wait() and pthread_cond_signal()

我想知道使用三个线程会如何。想象一下线程 1 想告诉线程 2 和线程 3 唤醒

例如

pthread_mutex_t mutex;
pthread_cond_t condition;

线程 1

pthread_mutex_lock(&mutex);

/*Initialize things*/

pthread_mutex_unlock(&mutex);
pthread_cond_signal(&condition); //wake up thread 2 & 3

/*Do other things*/

线程 2

pthread_mutex_lock(&mutex); //mutex lock
while(!condition)
    pthread_cond_wait(&condition, &mutex); //wait for the condition

pthread_mutex_unlock(&mutex);

/*Do work*/

线程 3

pthread_mutex_lock(&mutex); //mutex lock
while(!condition)
    pthread_cond_wait(&condition, &mutex); //wait for the condition

pthread_mutex_unlock(&mutex);

/*Do work*/

我想知道这样的设置是否有效。假设线程 2 和 3 依赖于线程 1 需要处理的一些初始化选项。

【问题讨论】:

你可以让两个线程在相同的条件下等待,是的。当您调用pthread_cond_signal() 时,只有其中一个会被唤醒,但被唤醒的线程可能会再次发出信号以唤醒下一个等待的线程......或者您可以使用pthread_cond_broadcast() 来唤醒所有等待条件的线程。 【参考方案1】:

首先:如果你希望线程#1唤醒线程#2#3,它应该使用pthread_cond_broadcast

第二:设置有效(有广播)。线程#2#3 计划唤醒,它们将尝试重新获取互斥锁作为唤醒的一部分。其中一个会,另一个将不得不等待互斥锁再次解锁。所以线程#2#3 依次访问临界区(重新评估条件)。

【讨论】:

投反对票。没有解决while(!condition) 使用pthread_cond_t 类型的变量作为布尔值的严重问题,这是没有意义的。 pthread_cond_t 是一种不透明类型——它可以是任何东西,而且它不太可能具有用户可能期望的布尔值。其他答案解释了如何正确使用pthread_cond_wait()【参考方案2】:

如果我理解正确,您希望 thr#2 和 thr#3(“workers”)阻塞,直到 thr#1(“boss”)执行一些初始化。

您的方法几乎可行,但您需要广播而不是信号,并且缺少与条件变量分开的谓词变量。 (在您引用的问题中,谓词和条件变量的名称非常相似。)例如:

pthread_mutex_t mtx;
pthread_cond_t  cv;
int             initialized = 0;  // our predicate of interest, signaled via cv

...

// boss thread
  initialize_things();
  pthread_mutex_lock(&mtx);
  initialized = 1;
  pthread_cond_broadcast(&cv);
  pthread_mutex_unlock(&mtx);
  ...

// worker threads
  pthread_mutex_lock(&mtx);
  while (! initialized) 
    pthread_cond_wait(&cv, &mtx);
  
  pthread_mutex_unlock(&mtx);
  do_things();

这很常见,您可能希望将 mutex/cv/flag 组合成一个抽象。 (有关灵感,请参阅 Python 的 Event object。)POSIX barriers 这是同步线程的另一种方式:每个线程都等待直到所有线程“到达”。 pthread_once 是另一种方式,因为它只运行一次函数,无论有多少线程调用它。

【讨论】:

【参考方案3】:

pthread_cond_signal 唤醒一个等待cond 变量的(随机)线程。如果你想唤醒所有等待这个cond变量的线程,请使用pthread_cond_broadcast

【讨论】:

【参考方案4】:

根据您在关键会话上所做的事情,除了先前答案中的解决方案之外,可能还有另一种解决方案。

假设thread1首先执行(即它是创建者线程)并且假设thread2thread3在关键会话中没有对共享资源执行任何写入操作。在这种情况下,pthread_cond_wait 是在实际上不需要时强制一个线程等待另一个线程。

您可以使用pthread_rwlock_t 类型的读写互斥锁。基本上thread1 执行写锁,因此其他线程在尝试获取读锁时将被阻塞。

这个锁的功能不言自明:

//They return: 0 if OK, error number on failure
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
    const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

thread1 完成初始化时,它会解锁。其他线程将执行读锁,并且由于更多的读锁可以共存,它们可以同时执行。再次重申:如果您不在线程 2 和 3 中对共享资源执行任何写入,则这是有效的。

【讨论】:

以上是关于pthread_cond_wait() 同时唤醒两个线程的主要内容,如果未能解决你的问题,请参考以下文章

Linux C语言 pthread_cond_wait()pthread_cond_timedwait()函数(不允许cond被唤醒时产生竞争,所以需要和互斥锁搭配)

互斥量和条件变量

什么是多线程“伪唤醒“以及为什么要用while循环进行wait判断

C-pthread_cond_wait 详解

pthread为啥规定cond要和mutex一起使用

Linux中 条件变量为啥要用互斥锁来保护?