记一次由虚假唤醒产生的bug

记一次由虚假唤醒产生的bug

int a代表产品数量最少0最多10,有两个生产者,三个消费者,用多线程和条件变量模拟生产消费过程:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <pthread.h>
//产品0-10
int a = 0;
//条件变量,互斥变量
pthread_cond_t cond_produce, cond_consume;
pthread_mutex_t mutex;

//生产过程
void *produce(void *argv)
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        if(a >= 10)
            pthread_cond_wait(&cond_produce, &mutex);
        ++a;
        printf("%s生产后:%d\n",(char*)argv, a);
        pthread_cond_signal(&cond_consume);
        pthread_mutex_unlock(&mutex);

    }
    return NULL;
}

//消费过程
void *consume(void *argv)
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        
        if(a <= 0)
            pthread_cond_wait(&cond_consume, &mutex);
        // 消费产品
        --a;
        printf("%s消费后:%d\n",(char*)argv, a);
        //唤醒生产者
        pthread_cond_signal(&cond_produce);
        
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

//初始化两个生产者和三个消费者
void initiate(pthread_t *producer1, pthread_t *producer2, pthread_t *customer1, 
              pthread_t *customer2, pthread_t *customer3)
{
    pthread_cond_init(&cond_produce, NULL);
    pthread_cond_init(&cond_consume, NULL);
    pthread_mutex_init(&mutex, NULL);
    //两个生产者,三个消费者
    pthread_create(producer1, NULL, produce, "生产者1");
    pthread_create(producer2, NULL, produce, "生产者2");
    pthread_create(customer1, NULL, consume, "消费者1");
    pthread_create(customer2, NULL, consume, "消费者2");
    pthread_create(customer3, NULL, consume, "消费者3");
    pthread_detach(*producer1);
    pthread_detach(*producer2);
    pthread_detach(*customer1);
    pthread_detach(*customer2);
    pthread_detach(*customer3);

}

int main() {
    pthread_t producer1 = 0, producer2 = 0, customer1 = 0, 
              customer2 = 0, customer3 = 0;
    initiate(&producer1, &producer2, &customer1, &customer2, &customer3);

    getchar();
    pthread_cond_destroy(&cond_consume);
    pthread_cond_destroy(&cond_produce);
    pthread_mutex_destroy(&mutex);
    return 0;
}

运行结果:

·····
生产者2生产后:11
消费者1消费后:10
消费者1消费后:9
消费者1消费后:8
消费者1消费后:7
消费者1消费后:6
消费者1消费后:5
消费者1消费后:4
消费者1消费后:3
消费者1消费后:2
消费者1消费后:1
消费者1消费后:0
消费者2消费后:-1

发现产品a会超过10也会小于0,刚开始以为pthread_cond_signal()会产生惊群效应,但后来实验了一下并不会,再查资料就知道了虚假唤醒

维基百科翻译:

当线程从等待已发出信号的条件变量中唤醒时,就会发生虚假唤醒,但发现它正在等待的条件未得到满足。 它被称为虚假的,因为线程似乎无缘无故地被唤醒了.但虚假的唤醒不会无缘无故地发生:它们通常发生是因为,在条件变量发出信号的时间和等待线程最终运行的时间之间,另一个线程运行并更改了条件.线程之间存在争用条件,典型的结果是,有时,在条件变量上唤醒的线程首先运行,赢得比赛,有时它运行第二,输掉比赛。

在许多系统上,特别是多处理器系统上,虚假唤醒的问题会加剧,因为如果在发出信号时有多个线程等待条件变量,系统可能会决定将它们全部唤醒,将每个唤醒一个线程视为唤醒所有线程,从而打破了信号和唤醒之间任何可能预期的1:1关系。 如果有十个线程在等待,则只有一个线程获胜,其他九个线程将经历虚假唤醒。

posted @ 2022-01-21 16:47  hellozhangjz  阅读(52)  评论(0编辑  收藏  举报