您的位置:首页 > 其它

多线程的生产者和消费者问题

2015-06-12 14:51 120 查看
多线程的生产者和消费者问题是比较经典的多线程问题,如果知道编码解决生产者和消费者问题,那么对于多线程应该基本算掌握了。我不知道大家的生产者和消费者问题是怎么样的(应该有几个版本吧),这里我说下我的生产者和消费者问题:

有多个生产线程,他们只负责生产资源;有多个消费者,同样他们只负责消费。但是他们需要满足下面条件:

生产者:只有当资源没有的时候,生产者才开始生产,但是为了防止浪费,每次只能生产指定数量的资源。当生产者生产完足够资源时,就进入睡眠,同时叫醒消费者去消费;

消费者:只有有资源时消费者才会去消费,当然每次消费多少可以自己设置。如果发现资源消费完了,则要马上叫生产者去生产,而消费者马上进入睡眠;



涉及到的知识:线程基础知识、多线程同步、互斥量、条件变量;可以参考: linux环境编程之多线程同步

生产者程序:

#include<pthread.h>
 #include<stdio.h>
 #include<stdlib.h>
 #include<fcntl.h>
 
 static int source = 0;  // 定义资源为个整型数,当然也可以定义为一个结构体
 static pthread_mutex_t lock; // 定义个互斥量用来锁住资源
 static pthread_cond_t con; // 定义条件变量
 
 static pthread_t pro_t1, pro_t2, con_t1, con_t2, con_t3, con_t4;
 
 void* product(void *arg)
 {
     while(1){
         if (!pthread_mutex_lock(&lock)){
             if (0 == source){
                 source += 3; // 每次都生产3个资源
                 sleep(1); // 为了查看打印的数据而已
                 // 查看下是哪个生产者来生产的
                 if (pro_t1 == pthread_self()) 
                     printf("\n\npro1 make source %d ==> %d\n", source-3, source);
                 else if (pro_t2 == pthread_self())
                     printf("\n\npro2 make source %d ==> %d\n", source-3, source);
                 else 
                     printf("error pro\n");
                 // 已经改变了条件,把条件变量上的等待线程都唤醒,让他们自行抢占
                 pthread_cond_broadcast(&con);
                 pthread_cond_wait(&con, &lock);// 生产者生产完休息
             }   
             pthread_mutex_unlock(&lock);
         }   
         usleep(500);// 为了防止总是同一个生产者去生产,也给消费者时间去消费
     }   
     return (void*)0;
 }

生产者分析:

首先进入死循环,不停的抢锁,抢到锁后查看下是否符合条件:

不符合则解锁,等0.5秒,让其他线程去抢占(主要是让消费者抢到锁去消费)。其实这里有个问题,如果有多个生产者,而且刚好每次都是生产者得到锁,那么就是个死锁了。虽然这种可能性比较小,但是这也有可能。就算不产生死锁,也会影响性能,让不相关的线程去抢占锁(上锁 == 判断 == 解锁 会消耗时间);

符合条件,则抢到锁的生产线程负责生产资源,生产完后留下姓名,最后叫醒消费线程去消费,自己则睡眠休息;这同样有个问题:就是生产线程会去捣乱,会去抢锁,然后判断,解锁。这会降低系统性能,后期可以想办法改进下。

当资源条件改变后,则生产线程(上次生产的那个线程)会被叫醒,这时候锁都在他手上。然后解锁,让大家去抢锁,这里延迟了0.5秒,是不让这个线程再去抢锁了(0.5秒虽然很长,但也有可能还是上次生产的那个线程获取到了锁),同样这也有上面的问题;

消费者程序:

void* consume(void *arg)
 {
     while(1){
         if (!pthread_mutex_lock(&lock)){// 抢锁
             if (source){ 
                 source--;// 消费资源
                 sleep(1);
                  // 打印谁消费了资源
                 if (con_t1 == pthread_self())
                     printf("con1 make source %d ==> %d\n", source+1, source);
                 else if (con_t2 == pthread_self())
                     printf("con2 make source %d ==> %d\n", source+1, source);
                 else if (con_t3 == pthread_self())
                     printf("con3 make source %d ==> %d\n", source+1, source);
                 else if (con_t4 == pthread_self())
                     printf("con4 make source %d ==> %d\n", source+1, source);
                 else
                     printf("error con\n");
 
                 if(!source){// 查看下资源是否还有
                     pthread_cond_broadcast(&con);// 如果没有资源了,则通知生产者开始工作了
                     pthread_cond_wait(&con, &lock);// 消费者进入睡眠等待生产者生产好资源来
                 }
             }
             pthread_mutex_unlock(&lock);
         }
         usleep(500);// 给生产者时间去生产,或者给其他消费者机会去获取锁去消费
     }
     return (void*)0;
 }
消费者分析:

和生产者类似,这里就不详细分析了。首先抢占锁,判断是否有资源,没有的话则等生产着去生产。如果有,则进入消费,消费完后再判断下是否把资源全部消耗掉了,如果没有,则让其他线程进入消费;如果有,则通知生产线程去生产,而自己休眠等待通知;

缺陷:

这个解决方案有两个缺陷:

1、多生产者时,当生产完后,该生产资源的线程睡眠(同时唤醒所有消费),但其他生产线程会和消费线程抢锁。多消费者,同样的情况;

2、开始的时候其实是没有唤醒,比如消费线程开始的时候没有资源,不是通知生产者生产(这时候生产线程也没有休眠,就算通知也没有用),而是循环等待,希望生产线程能获取到锁去生产。

解决:

解决方法可以试试先判断资源条件是否符合,符合进入上锁然后操作,不符合则等待睡眠。但因为条件检查和上锁不是原子操作,容易变成死锁。还有其他一些问题。后期再研究下,或者大家帮忙解决下(不过自己研究会更有乐趣,可以提供思路)

主函数程序:

int main(int argc, char* argv[])
 {
     pthread_attr_t attr; // 互斥量锁属性
     pthread_attr_init(&attr);// 初始化互斥量锁属性
 
     pthread_mutex_init(&lock, NULL);// 初始化互斥量锁
     pthread_cond_init(&con, NULL); // 初始化条件变量
 
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);// 设置锁属性
 
     pthread_create(&con_t1, &attr, &consume, NULL);// 创建消费线程和生产线程
     pthread_create(&con_t2, &attr, &consume, NULL);
     pthread_create(&con_t3, &attr, &consume, NULL);
     pthread_create(&con_t4, &attr, &consume, NULL);
     pthread_create(&pro_t1, &attr, &product, NULL);
     pthread_create(&pro_t2, &attr, &product, NULL);
 
     pause();// 挂起主线程,主线程不能死
     pthread_cond_destroy(&con);// 下面都是注销函数,做扫尾工作
     pthread_mutex_destroy(&lock);
     pthread_attr_destroy(&attr);
 
     return 0;
 }

转载地址:/article/1530592.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: