【问题标题】:Unexpected behavior while using condition variable multithread使用条件变量多线程时出现意外行为
【发布时间】:2015-05-26 13:28:19
【问题描述】:

在下面的代码中:

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mtx;
pthread_cond_t cond;

int how_many = 10;
int pool = 0;

void * producer(void * ptr)
{
        while (how_many > 0)
        {
                pthread_mutex_lock(&mtx);
                printf("producer: %d\n", how_many);
                pool = how_many;
                how_many--;
                pthread_mutex_unlock(&mtx);
                pthread_cond_signal(&cond);
        }

        pthread_exit(0);
}

void * consumer(void * ptr)
{
        while (how_many > 0)
        {
                pthread_mutex_lock(&mtx);
                pthread_cond_wait(&cond, &mtx);
                printf("consumer: %d\n", pool);
                pool = 0;
                pthread_mutex_unlock(&mtx);
        }
        pthread_exit(0);
}

int main(int argc, char ** argv)
{
        pthread_t prod, cons;
        pthread_mutex_init(&mtx, 0);
        pthread_cond_init(&cond, 0);
        pthread_create(&cons, 0, consumer, 0);
        pthread_create(&prod, 0, producer, 0);
        pthread_join(prod, 0);
        pthread_join(cons, 0);
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mtx);
        return 0;
}

我没有得到预期的输出。

预期输出:

Producer:10    
Consumer:10    
Producer:9    
Consumer:9    
Producer:8    
Consumer:8    
Producer:7    
Consumer:7    
Producer:6    
Consumer:6    
Producer:5    
Consumer:5    
Producer:4    
Consumer:4    
Producer:3    
Consumer:3    
Producer:2    
Consumer:2    
Producer:1    
Consumer:1

实际输出:

producer: 10    
producer: 9    
producer: 8    
producer: 7    
producer: 6    
producer: 5    
producer: 4    
producer: 3    
producer: 2    
producer: 1

另外,在消费者端,如果我们锁定并等待信号,生产者如何获得锁定以便他可以将信号发送给消费者?

  1. 会死锁吗?
  2. 我的朋友都在推荐pthread_cond_wait(&amp;cond, &amp;mtx);实际上会解锁资源,直到它从生产者那里得到信号。这是真的吗?

【问题讨论】:

  • 您想从临界区内部发出条件信号,即解锁相关互斥体之前。
  • 我的朋友们建议……这是真的吗?”根据文档,是的。
  • 我在生产者端释放锁后使用了 sleep(1),它工作正常:-) 我理解 pthread_cond_wait() 在收到信号之前不会持有锁。

标签: c multithreading mutex condition-variable


【解决方案1】:

互斥体只提供互斥(如果使用得当);它们本身并不提供阻止特定事件发生或直到满足特定条件的机制。这就是条件变量的用途(还有信号量,如果你想降低一点)。

您的代码让消费者等待生产者生产,但不让生产者在继续之前等待消费者消费。如果您希望两个线程交替进行,那么您需要第二个条件变量来为后者提供。

另外,在消费者端,如果我们锁定并等待信号,生产者如何获得锁定以便他可以将信号发送给消费者?

  1. 会死锁吗?

  2. 我的朋友建议像 pthread_cond_wait(&cond, &mtx);实际上会解锁资源,直到它从生产者那里得到信号。这是真的吗?

您是否考虑过阅读文档,而不是询问您的朋友(或互联网)?以下是手册页的描述方式:

这些函数以原子方式释放互斥体 [...]。成功返回后,互斥体应已被锁定并归调用线程所有。

也就是说,调用pthread_cond_wait()的线程在等待时不会保持互斥锁锁定,而是在返回之前重新获取互斥锁(这可能涉及线程接收信号和函数调用返回之间的不确定延迟) .

此外,请始终记住,线程可能会因等待条件变量而被虚假唤醒。必须在唤醒时检查条件是否确实满足,如果不满足则继续等待。

您可以通过以下方式构建生产者:

void * producer(void * ptr)
{
        pthread_mutex_lock(&mtx);
        while (how_many > 0)
        {
                if (pool == 0) {
                        printf("producer: %d\n", how_many);
                        pool = how_many;
                        how_many--;
                        pthread_cond_signal(&full_cond);
                }
                pthread_cond_wait(&empty_cond, &mtx);
        }
        pthread_mutex_unlock(&mtx);

        pthread_exit(0);
}

注意:

  1. 我已经重命名了您原来的条件变量并引入了一个新的。现在有full_cond,表示池(容量为1)已满,empty_cond,表示池为空。
  2. 整个循环由互斥体保护。这很好,因为它执行pthread_cond_wait() 命名该互斥锁;其他线程将能够在生产者等待时运行。互斥锁确保对how_manypool 变量的访问正确同步。
  3. 该循环通过测试pool 来验证它是否确实为空,从而防止虚假唤醒。如果没有,它会循环回到等待状态而不做任何其他事情。
  4. 为了使其正常工作,消费者需要进行相应的更改(留给您作为练习)。

【讨论】:

  • 谢谢@John Bollinger
  • 我知道 pthread_cond_wait() 在返回 true 之前不会持有锁 :-)
【解决方案2】:

您正在检查how_many 出锁定部分。您需要重组代码,以便读取变量被锁覆盖,或者它是 C11 _Atomic

即使那样,您的代码输出也可能不会是您想要的方式,因为线程的调度几乎是不可预测的。

【讨论】:

    【解决方案3】:

    对于您的预期输出,您可以使用如下锁定机制,

     #include <pthread.h> 
     #include <stdio.h> 
     #include <stdlib.h> 
     #include <semaphore.h> 
    
     sem_t mutex1; 
     sem_t mutex2; 
     int main()
     {
        pthread_t thread1, thread2; 
        sem_init(&mutex1, 0, 1); 
        sem_init(&mutex2, 0, 0);
        pthread_create( &thread1, NULL, &producer, NULL)
        pthread_create( &thread2, NULL, &consumer, NULL)
        pthread_join( thread1, NULL); 
        pthread_join( thread2, NULL); 
        return 0; 
     }
    
    
    void producer()
    {
      sem_wait(&mutex1); 
      :
      :
    
      sem_post(&mutex2);
    }
    
    void consumer ()
    {
        sem_wait(&mutex2); 
        :
        : 
        sem_post(&mutex1);
    }   
    

    【讨论】:

      猜你喜欢
      • 2012-12-14
      • 2017-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-18
      • 2016-05-24
      • 2017-03-16
      • 2018-10-08
      相关资源
      最近更新 更多