什么'如果pthread_cond_wait自己这样做,那么锁定和解锁互斥锁的意义就在于什么

What's the point of locking and unlocking a mutex if pthread_cond_wait does that itself?

本文关键字:什么 解锁 就在于 锁定 如果 pthread cond 这样做 自己 wait      更新时间:2023-10-16

我试图了解互斥和条件变量之间的区别,但被下面的代码弄糊涂了。

// Lock mutex and then wait for signal to relase mutex
pthread_mutex_lock( &count_mutex );
/*Wait while functionCount2() operates on count
mutex unlocked if condition varialbe in functionCount2() signaled. <-- so then why call pthread_mutex_unlock() 3 lines latter if it's already unlocked?*/
pthread_cond_wait( &condition_var, &count_mutex );
count++;
printf("Counter value functionCount1: %dn",count);
pthread_mutex_unlock( &count_mutex );

根据文档pthread_cond_wait()"原子释放互斥并导致调用线程阻塞条件变量",那么如果pthread_cond_wait()只是解锁它,pthread_mutex_lock( &count_mutex );有什么意义?既然pthread_cond_wait()已经解锁了它,那么后者调用pthread_mutex_unlock()有什么意义呢?

对我来说,如果pthread_cond_wait()不接受互斥,这是有意义的,我认为这就是它的意义所在。

首先让我们定义一些可能有点混淆的术语:

  • condition:这是一个线程用来确定是否发生了什么事情或是否需要完成工作的表达式。例如,一个条件可能是队列不为空。

  • 条件变量:这是pthread库(或类似库)提供的一个同步对象,用于让线程等待条件更改。一个线程可以"等待"一个条件变量,然后当另一个线程发出该条件变量的信号时,等待的线程将唤醒。

请注意,"条件"与"条件变量"是不同的。

当使用条件变量时,线程需要检查条件(无论是什么),然后如果条件不满足,它可以等待条件变量,直到有信号表明它应该再次检查条件。

然而,这一系列事件:

  1. 检查状况
  2. 等待条件变量

它本身并不是原子性的——如果在步骤1和步骤2之间,另一个线程发出条件变量的信号,那么等待的线程可能永远不会醒来(条件变量不记得过去发生过信号;当发出信号时,它们只会解锁已经在等待的线程)。为了避免这个问题,条件变量必须与确保这两个步骤相对于处理条件和条件变量的其他线程以原子方式发生的模式一起使用。这种模式是:

  • 任何读取或更新条件中使用的对象的线程在执行此操作时都必须持有互斥对象。请注意,这包括可能等待条件变量的线程
  • 在等待条件变量时,在持有互斥对象时检查条件的线程必须继续持有pthread_cond_wait()调用的互斥对象。这确保了上述步骤1和2之间的竞争条件不会发生,因为在pthread_cond_wait()调用准备好线程等待之前,任何更新条件的东西都无法获取互斥。此时,pthread_cond_wait()函数将释放互斥,这将允许更新线程获取互斥并更新条件

让我们看看以下两个函数,等待通知。两者都在两个独立的线程中运行,并使用全局定义的变量valuemutexcond。让我们假设等待线程首先启动并锁定互斥锁。现在可以安全地测试条件"值为零",因为锁保证没有其他线程可以更改。在下一步中,我们的等待线程正在条件变量上等待通知函数的信号,即已更改,应再次检查。

void* waiting(void* arg)
{
  pthread_mutex_lock(&mutex);
  while(value == 0)
  {
    pthread_cond_wait(&cond, &mutex);
  }
  printf("waiter %d releasesn", *tid);
  pthread_mutex_unlock(&mutex);
}

现在让我们看看通知功能的作用。您还记得我们已经将互斥锁锁定在等待线程中吗?如果不释放它,我们的通知函数永远无法获得互斥并更改。这就是pthread_cond_wait释放它的原因。不,函数不会最终释放它,只是在函数被阻塞的那段时间。现在,我们的通知线程可以更改,并向pthread_cond_wait发送一个信号,表示发生了一些事情。当pthread_cond_wait解除锁定(返回)时,互斥锁将再次锁定!

void* notifying(void* arg)
{
  pthread_mutex_lock(&mutex);
  value = 1;
  pthread_cond_signal(&cond);
  pthread_mutex_unlock(&mutex);
}

有趣的是,在循环中等待条件变量,反复检查条件是很常见的。为什么?有时pthread_cond_wait在没有从通知函数获得信号的情况下解除阻塞(所谓的杂散信号)。