zoukankan      html  css  js  c++  java
  • 01多线程 ---- 条件变量(生产者消费者问题)

    • 假设有一个生产者线程,一个消费者线程,生产一个,消费一个。我们来看看怎么实现。
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    int buffer;
    int count = 0;
    
    void put(int value)
    {
            assert(count == 0);
            count = 1;
            buffer = value;
    }
    
    int get()
    {
            assert(count == 1);
            count = 0;
            return buffer;
    }
    
    void *producer(void *arg)
    {
            printf("producer...
    ");
            int i;
            int loops = (int) arg;
            for (i = 0; i < loops; i++) {
                    put(i);
                    printf("put:%d
    ",i);
            }
    }
    
    void *consumer(void *arg)
    {
            printf("consumer...
    ");
            int i;
            while(1) {
                    int tmp = get();
                    printf("%d
    ",tmp);
            }
    }
    
    int main()
    {
            printf("begin...
    ");
            pthread_t p;
            pthread_create(&p, NULL, consumer,(void *)100);
            producer((void*)100);
            pthread_join(p, NULL);
            return 0;
    }

    上面的代码运行,显然不能满足我们要求,这个时候就需要条件变量。

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    int buffer;
    int count = 0;
    
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    
    void put(int value) 
    {
        assert(count == 0);
        count = 1;
        buffer = value;
    }
    
    int get()
    {
        assert(count == 1);
        count = 0;
        return buffer;
    }
    
    void *producer(void *arg)
    {
        printf("producer...
    ");
        int i;
        int loops = (int) arg;
        for (i = 0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            if(count == 1)
                pthread_cond_wait(&cond, &mutex);
            put(i);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
            printf("put:%d
    ",i);
        }
    }
    
    void *consumer(void *arg)
    {
        printf("consumer...
    ");
        int i;
        int loops = (int)arg;
        for(i =0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            if(count == 0)
                pthread_cond_wait(&cond, &mutex);
            int tmp = get();
            printf("%d
    ",tmp);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
        }
    }
    [zf@localhost ch30]$ ./a.out 
    begin...
    producer...
    put:0
    consumer...
    0
    put:1
    1
    put:2
    2
    put:3
    3
    put:4
    4

    结果来看是满足要求的,可是如果有两个消费者呢?

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    int buffer;
    int count = 0;
    
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    
    void put(int value) 
    {
        assert(count == 0);
        count = 1;
        buffer = value;
    }
    
    int get()
    {
        assert(count == 1);
        count = 0;
        return buffer;
    }
    
    void *producer(void *arg)
    {
        printf("producer...
    ");
        int i;
        int loops = (int) arg;
        for (i = 0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            if(count == 1)
                pthread_cond_wait(&cond, &mutex);
            put(i);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
            printf("put: %d",i);
        }
    }
    
    void *consumer(void *arg)
    {
        printf("consumer...
    ");
        int i;
        int loops = (int)arg;
        for(i =0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            if(count == 0)
                pthread_cond_wait(&cond, &mutex);
            int tmp = get();
            printf("[%d]get: %d
    ",pthread_self(),tmp);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
        }
    }
    
    int main()
    {
        printf("begin...
    ");
         pthread_t p,p1;
        pthread_create(&p, NULL, consumer,(void *)10000);
        pthread_create(&p1, NULL, consumer, (void*)10000);
    
        producer((void*)20000);
        pthread_join(p, NULL);
        pthread_join(p1, NULL);
        return 0;
    }
    put: 1207[1848375040]get: 1207
    put: 1208[1839982336]get: 1208
    put: 1209[1848375040]get: 1209
    put: 1210[1839982336]get: 1210
    put: 1211[1848375040]get: 1211
    a.out: main.c:21: get: Assertion `count == 1' failed.
    已放弃(吐核)

    我们发现失败了,这是为什么呢?我们来思考一下,首先和if语句有关系。就是消费者Tc1被信号唤醒的时候,刚好被Tc2抢占,消耗了缓存,导致Tc1再消耗的时候会触发断言,这个问题应该怎么办呢?其实很简单,用while代替if,我们再次检查一下容量。

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    int buffer;
    int count = 0;
    
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    
    void put(int value) 
    {
        assert(count == 0);
        count = 1;
        buffer = value;
    }
    
    int get()
    {
        assert(count == 1);
        count = 0;
        return buffer;
    }
    
    void *producer(void *arg)
    {
        printf("producer...
    ");
        int i;
        int loops = (int) arg;
        for (i = 0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == 1)
                pthread_cond_wait(&cond, &mutex);
            put(i);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
            printf("put: %d",i);
        }
    }
    
    void *consumer(void *arg)
    {
        printf("consumer...
    ");
        int i;
        int loops = (int)arg;
        for(i =0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == 0)
                pthread_cond_wait(&cond, &mutex);
            int tmp = get();
            printf("[%d]get: %d
    ",pthread_self(),tmp);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
        }
    }
    
    int main()
    {
        printf("begin...
    ");
         pthread_t p,p1;
        pthread_create(&p, NULL, consumer,(void *)10000);
        pthread_create(&p1, NULL, consumer, (void*)10000);
    
        producer((void*)20000);
        pthread_join(p, NULL);
        pthread_join(p1, NULL);
        return 0;
    }
    put: 1276[1475249920]get: 1276
    put: 1277[1466857216]get: 1277
    put: 1278[1475249920]get: 1278
    put: 1279[1466857216]get: 1279
    put: 1280[1475249920]get: 1280
    put: 1281[1466857216]get: 1281
    put: 1282[1466857216]get: 1282
    put: 1283[1475249920]get: 1283
    put: 1284[1466857216]get: 1284
    put: 1285[1466857216]get: 1285

    可是结果并非预期的那样,发现程序执行到一半不动了,卡住了。这是为什么呢?我们加打印看一下

    void *producer(void *arg)
    {
        printf("producer...
    ");
        int i;
        int loops = (int) arg;
        for (i = 0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == 1){
                printf("producer wait......
    ");
                pthread_cond_wait(&cond, &mutex);
            }
            put(i);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
            printf("put: %d",i);
        }
    }
    
    void *consumer(void *arg)
    {
        printf("consumer...
    ");
        int i;
        int loops = (int)arg;
        for(i =0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == 0){
                printf("consumer wait.....
    ");
                pthread_cond_wait(&cond, &mutex);
            }
            int tmp = get();
            printf("[%d]get: %d
    ",pthread_self(),tmp);
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&mutex);
        }
    }
    put: 197producer wait......
    [632694528]get: 197
    consumer wait.....
    consumer wait.....

    这个发现一个问题,就是消费者T1消费后,唤醒的是T2,这样就三个线程都在休眠,这个真是个可怕的问题,我们应该怎么办呢?按道理,消费者消费后应该唤醒生产者,所以消费者和生产者应该发出不同信号,这样不就好了吗

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    int buffer;
    int count = 0;
    
    pthread_cond_t empty,fill;
    pthread_mutex_t mutex;
    
    void put(int value) 
    {
        assert(count == 0);
        count = 1;
        buffer = value;
    }
    
    int get()
    {
        assert(count == 1);
        count = 0;
        return buffer;
    }
    
    void *producer(void *arg)
    {
        printf("producer...
    ");
        int i;
        int loops = (int) arg;
        for (i = 0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == 1){
                printf("producer wait......
    ");
                pthread_cond_wait(&empty, &mutex);
            }
            put(i);
            pthread_cond_signal(&fill);
            pthread_mutex_unlock(&mutex);
            printf("put: %d",i);
        }
    }
    
    void *consumer(void *arg)
    {
        printf("consumer...
    ");
        int i;
        int loops = (int)arg;
        for(i =0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == 0){
                printf("consumer wait.....
    ");
                pthread_cond_wait(&fill, &mutex);
            }
            int tmp = get();
            printf("[%d]get: %d
    ",pthread_self(),tmp);
            pthread_cond_signal(&empty);
            pthread_mutex_unlock(&mutex);
        }
    }
    
    int main()
    {
        printf("begin...
    ");
         pthread_t p,p1;
        pthread_create(&p, NULL, consumer,(void *)10000);
        pthread_create(&p1, NULL, consumer, (void*)10000);
    
        producer((void*)20000);
        pthread_join(p, NULL);
        pthread_join(p1, NULL);
        return 0;
    }
    put: 19997producer wait......
    [754308864]get: 19997
    consumer wait.....
    put: 19998producer wait......
    [754308864]get: 19998
    consumer wait.....
    put: 19999[754308864]get: 19999

    这里,两个问题就都解决了,是不是很happy。别高兴太早,这个方案虽然可用了,但不太通用,我们buffer不可能只是一个的,下面我们再改改

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    #define MAX 1000
    
    int buffer[MAX];
    int fills = 0;
    int use   = 0;
    int count = 0;
    
    pthread_cond_t empty,fill;
    pthread_mutex_t mutex;
    
    void put(int value) 
    {
        buffer[fills] = value;
        fills = (fills + 1) % MAX;
        count++;
    }
    
    int get()
    {
        int tmp = buffer[use];
        use = (use + 1) % MAX;
        count--;
        return tmp;
    }
    
    void *producer(void *arg)
    {
        printf("producer...
    ");
        int i;
        int loops = (int) arg;
        for (i = 0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == MAX){
                printf("producer wait......
    ");
                pthread_cond_wait(&empty, &mutex);
            }
            put(i);
            pthread_cond_signal(&fill);
            pthread_mutex_unlock(&mutex);
            printf("put: %d",i);
        }
    }
    
    void *consumer(void *arg)
    {
        printf("consumer...
    ");
        int i;
        int loops = (int)arg;
        for(i =0; i < loops; i++) {
            pthread_mutex_lock(&mutex);
            while(count == 0){
                printf("consumer wait.....
    ");
                pthread_cond_wait(&fill, &mutex);
            }
            int tmp = get();
            printf("[%d]get: %d
    ",pthread_self(),tmp);
            pthread_cond_signal(&empty);
            pthread_mutex_unlock(&mutex);
        }
    }
    
    int main()
    {
        printf("begin...
    ");
         pthread_t p,p1;
        pthread_create(&p, NULL, consumer,(void *)10000);
        pthread_create(&p1, NULL, consumer, (void*)10000);
    
        producer((void*)20000);
        pthread_join(p, NULL);
        pthread_join(p1, NULL);
        return 0;
    }
    [-797354240]get: 19988
    [-797354240]get: 19989
    [-797354240]get: 19990
    [-797354240]get: 19991
    [-797354240]get: 19992
    [-797354240]get: 19993
    [-797354240]get: 19994
    [-797354240]get: 19995
    consumer wait.....
    put: 19996put: 19997put: 19998put: 19999[-797354240]get: 19996
    [-797354240]get: 19997
    [-797354240]get: 19998
    [-797354240]get: 19999

    这样就差不多了哈,确实快了呢!!!嘿嘿

    参考:

    • 操作系统导论
  • 相关阅读:
    linux学习方法之一
    HDU 1556 Color the ball
    Object-c学习之路十(NSNumber&NSValue)
    蜂鸣器驱动方式源程序--有源无源通用
    Wordpress更换主题之后出错
    mybatis_Generator配置
    Logistic Regression
    求两个字符串的最大公共字串
    数据结构排序系列详解之二 希尔排序
    《mysql必知必会》学习_第五章
  • 原文地址:https://www.cnblogs.com/vczf/p/11886512.html
Copyright © 2011-2022 走看看