【问题标题】:Condition variables and mutex_unlock条件变量和 mutex_unlock
【发布时间】:2015-06-01 22:54:49
【问题描述】:

代码:

void *inc_func(void *arg)
{
    pthread_mutex_lock(&mutex);
    pthread_cond_signal(&count_threshold_cv);
    sleep(1);
    pthread_mutex_unlock(&mutex);
}
void *watch(void *arg)
{
    pthread_mutex_lock(&mutex);
    pthread_cond_wait(&count_threshold_cv,&mutex);
    sleep(1);
    pthread_mutex_unlock(&mutex);
}
int main()
{
    pthread_t id[2];
    pthread_create(&id[0],NULL,watch,NULL);
    pthread_create(&id[1],NULL,inc_func,NULL);
    int i;
    for(i=0;i<2;i++)
        pthread_join(id[i],NULL);
}

现在我们有一个mutex_unlock 函数要在每个线程中执行。和一个锁定的互斥锁。为什么这不会导致undefined behaviour。由于两个线程都尝试解锁同一个互斥锁,这导致一个线程尝试解锁已经解锁的互斥锁。

编辑:pthread_cond_wait 释放 mutex 以供第二个线程使用。现在考虑第二个线程执行pthread_cond_signal,这导致第一个线程重新获取mutex。现在我们有两个线程具有相同的mutex 锁,因为'sleep' 功能两个线程都没有执行mutex_unlock。是不是我的理解错了?

【问题讨论】:

标签: c pthreads mutex condition-variable


【解决方案1】:
  • pthread_mutex_lock() 阻塞,如果要锁定的互斥体已经被锁定。如果互斥锁被解锁,它就会返回。

  • pthread_cond_wait() 在开始等待时解锁互斥锁,在返回之前锁定。如果有问题的互斥锁仍处于锁定状态,则返回可能会延迟。然后返回将延迟,直到互斥锁被解锁。

将以上内容放在一起并将其应用到您展示的代码中,您会发现每个线程函数都很好地以锁定开始,然后是解锁(等等),所以一切都很好。

参考示例代码:pthread_cond_wait()inc_func() 调用pthread_mutex_unlock() 时返回。


要成功处理示例代码描述的场景,您需要考虑两种特殊情况

  1. 信号先来的情况
  2. 所谓的"spurious wake-ups" 的情况,即pthread_cond_wait() 在没有收到信号的情况下返回。

要处理这两种情况,每个条件都应该有一个监视变量。

pthread_mutex_t mutex = ...
pthread_cond_t count_threshold_cv = ...

int signalled = 0;

void *inc_func(void *arg)   
{
  pthread_mutex_lock(&mutex);

  pthread_cond_signal(&count_threshold_cv);
  signalled = 1;

  pthread_mutex_unlock(&mutex);  
}

void *watch(void *arg)
{
  pthread_mutex_lock(&mutex);

  while (0 == signalled)
  {
    pthread_cond_wait(&count_threshold_cv,&mutex);
  }

  pthread_mutex_unlock(&mutex);
}

int main(void)
{
  pthread_t id[2];
  pthread_create(&id[0],NULL,watch,NULL);
  pthread_create(&id[1],NULL,inc_func,NULL);
  int i;
  for(i=0;i<2;i++)
    pthread_join(id[i],NULL);
}  

【讨论】:

  • 我认为这个问题改写得更好
  • @InQusitive:你错误地认为pthread_cond_wait() 是由于pthread_cond_signal() 被调用而返回的。事实并非如此。
  • 每个定义不能有两个线程持有相同的互斥锁。这就是他们的目的。 pthread_mutex_lock() 如果在已锁定的互斥体上调用,将简单地阻塞,不会返回。 @InQusitive
  • @InQusitive:您的真实代码是否初始化了它使用的互斥锁?您的真实代码是否会检查每个调用的 pthread_*() 函数的结果,测试它是否可能失败?
  • 是的,无论如何我对 mutex_cond_wait 的理解是错误的。现在很清楚了。 :) 请在答案中写下您的第二条评论。
【解决方案2】:

如果订单确实是

  • watch 先运行并锁定(inc_func 等待)
  • 具有互斥锁的watch 使用pthread_cond_wait 等待,它根据documentation 解锁互斥锁和块

这些函数以原子方式释放互斥体并导致调用线程 在条件变量 cond 上阻塞

这允许以下操作

  • inc_func 获取互斥体然后发出信号(此时互斥体尚未释放)
  • inc_func 释放互斥锁
  • 由于互斥锁已被释放并且互斥锁解锁,watch 的执行恢复已按照documentation 锁定互斥锁:

成功返回后,互斥体已被锁定并归调用线程所有。

  • 接下来是互斥锁的合法版本。

你没有考虑的场景是如果inc_func的代码先执行而不切换到watch会怎样

  • inc_func 锁定互斥体
  • inc_func 发出信号,但没有人发出信号,这没关系。
  • inc_func 解锁互斥锁
  • watch 锁定互斥体
  • 然后等待条件变量,但没有人发出信号,所以它会一直等待。

【讨论】:

    猜你喜欢
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 2020-06-02
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多