OS: 生产者消费者问题(多线程+互斥量+条件变量)

一. 引子

用多进程解决生产着消费者问题之后,再尝试多线程方法,才知道多线程多么地方便。多线程方案的易用性,一方面得益于强大的条件变量。赞,太好用了!

二. 思路

互斥量实际上相当于二元信号量,它是纯天然适合生产者消费者问题的解决方案,使用互斥量可以很好地描述生产者或者消费者独占缓冲区的特点。
不过互斥量的能力也仅此而已,如果需要在使用线程方案时提供更复杂的逻辑,则需要配合使用条件变量。生产者要求在缓冲区不满的情况下才能生产,我用 notFull 条件变量表示这种情况;消费者要求在缓冲区不空的情况下才能消费,我用 notEmpty 条件描述这种情况。

三. 互斥量

互斥量有两种初始化方法,

静态声明时

声明并直接赋值,pthread_mutex_t buf = PTHREAD_MUTEX_INITIALIZER;//用于静态分配的互斥量

动态分配时

当使用 malloc 等动态分配互斥量的空间时,使用 pthread_mutex_init 函数初始化,另需要用 pthread_mutex_destroy 销毁:
int pthread_mutex_init(pthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restrict attr);

(restrict 表示, attr 指向的值不能通过别的指针修改,编译器据此可以做出一定的优化)

[cpp]  view plain copy
  1. struct Share{  
  2.     int critical;//保护区的数据  
  3.     pthread_mutex_t access;//限制对 critical 访问的互斥量  
  4. };  
  5.   
  6. int main()  
  7. {  
  8.     struct Share * pSh = (struct Share * ) malloc(sizeof(struct Share));  
  9.     pSh->critical = 0;  
  10.     pthread_mutex_init(&(pSh->access),NULL);//NULL表示使用默认属性  
  11. }  



四. 条件变量

条件变量与互斥量配合以实现线程的同步,通常是以下结构:
[cpp]  view plain copy
  1. pthread_mutex_wait(&mtx);  
  2. while(condition not fullfilled){  
  3.     pthread_cond_wait(&cond,&mtx);  
  4. }  
  5. //在此处修改缓冲区  
  6. pthread_mutex_signal(&mtx);  

其中,cond 是 pthread_cond_t 类型的对象,mtx 是 pthread_mutex_t 类型的对象。pthread_cond_wait 在不同条件下行为不同:
1. 当执行 pthread_cond_wait 时,作为一个原子操作包含以下两步:
1) 解锁互斥量 mtx
2) 阻塞进程直到其它线程调用 pthread_cond_signal 以告知 cond 可以不阻塞

2. 当执行 pthread_cond_signal(&cond) 时,作为原子操作包含以下两步:
1) 给 mtx 加锁
2)停止阻塞线程, 因而得以再次执行循环,判断条件是否满足。(注意到此时 mtx 仍然被当前线程独有,保证互斥)

五. 代码描述

1. 缓冲区描述

[cpp]  view plain copy
  1. //从 begin 到 end(不含end) 代表产品  
  2. //cnt 代表产品数量  
  3. //max 代表库房的容量,即最多生产多少产品  
  4. int begin = 0,end = 0, cnt = 0, max = 4;  

2. 互斥机制

指同一时间只能有一个人访问缓冲区,用互斥量 buf 实现。
[cpp]  view plain copy
  1. pthread_mutex_t buf = PTHREAD_MUTEX_INITIALIZER;//用于锁住缓冲区  

用 pthread_mutex_lock(&buf) 独占缓冲区;pthread_mutex_unlock(&buf); 释放缓冲区

3. 同步机制

用条件变量配合互斥量实现,条件变量 notFull 和 notEmpty 与 buf 结合,使得在条件不满足的情况下,能够释放对缓冲区的占用,使得他人能够访问缓冲区。
[cpp]  view plain copy
  1. pthread_cond_t notFull,notEmpty;//缓冲区不满;缓冲区不空  
涉及到 pthread_cond_wait() 函数和 pthread_cond_signal 函数

2. 消费者行为

[cpp]  view plain copy
  1. while(1)  
  2. {  
  3.     pthread_mutex_lock(&buf);  
  4.     while(cnt == 0){//当缓冲区空时  
  5.         pthread_cond_wait(&notEmpty,&buf);  
  6.     }  
  7.     printf("consume %d\n",begin);  
  8.     begin = (begin+1)%max;  
  9.     cnt--;  
  10.     pthread_mutex_unlock(&buf);  
  11.     sleep(C_SLEEP);  
  12.     pthread_cond_signal(&notFull);  
  13. }  


3. 生产者行为

[cpp]  view plain copy
  1. while(1)  
  2. {  
  3.     pthread_mutex_lock(&buf);  
  4.     while(cnt == max){//当缓冲区满时  
  5.         pthread_cond_wait(&notFull,&buf);  
  6.     }  
  7.     printf("produce %d\n",end);  
  8.     end = (end+1)%max;  
  9.     cnt++;  
  10.     pthread_mutex_unlock(&buf);  
  11.     sleep(P_SLEEP);  
  12.     pthread_cond_signal(&notmpty);  
  13. }  



六. 代码

[cpp]  view plain copy
  1. #include "stdio.h"  
  2. #include <stdlib.h>  
  3. #include <pthread.h>  
  4.   
  5.   
  6. #define N_CONSUMER 3 //消费者数量  
  7. #define N_PRODUCER 2 //生产者数量  
  8. #define C_SLEEP 1 //控制 consumer 消费的节奏  
  9. #define P_SLEEP 1 //控制 producer 生产的节奏  
  10.   
  11. pthread_t ctid[N_CONSUMER];//consumer thread id  
  12. pthread_t ptid[N_PRODUCER];//producer thread id  
  13.   
  14. pthread_cond_t notFull,notEmpty;//缓冲区不满;缓冲区不空  
  15. pthread_mutex_t buf = PTHREAD_MUTEX_INITIALIZER;//用于锁住缓冲区  
  16.   
  17. //从 begin 到 end(不含end) 代表产品  
  18. //cnt 代表产品数量  
  19. //max 代表库房的容量,即最多生产多少产品  
  20. int begin = 0,end = 0, cnt = 0, max = 4;  
  21.   
  22. void * consumer(void * pidx)//consumer thread idx  
  23. {  
  24.     printf("consumer thread id %d\n",*((int *)pidx));  
  25.     while(1)  
  26.     {  
  27.         pthread_mutex_lock(&buf);  
  28.         while(cnt == 0){//当缓冲区空时  
  29.             pthread_cond_wait(¬Empty,&buf);  
  30.         }  
  31.         printf("consume %d\n",begin);  
  32.         begin = (begin+1)%max;  
  33.         cnt--;  
  34.         pthread_mutex_unlock(&buf);  
  35.         sleep(C_SLEEP);  
  36.         pthread_cond_signal(¬Full);  
  37.     }  
  38.     pthread_exit((void *)0);  
  39. }  
  40. void * producer(void * pidx)//producer thread idx  
  41. {  
  42.     printf("producer thread id %d\n",*((int *)pidx));  
  43.     while(1)  
  44.     {  
  45.         pthread_mutex_lock(&buf);  
  46.         while(cnt == max){//当缓冲区满时  
  47.             pthread_cond_wait(¬Full,&buf);  
  48.         }  
  49.         printf("produce %d\n",end);  
  50.         end = (end+1)%max;  
  51.         cnt++;  
  52.         pthread_mutex_unlock(&buf);  
  53.         sleep(P_SLEEP);  
  54.         pthread_cond_signal(¬Empty);  
  55.     }  
  56.     pthread_exit((void *)0);  
  57. }  
  58.   
  59. int main()  
  60. {  
  61.     int i  = 0;  
  62.     for(i = 0; i < N_CONSUMER; i++)  
  63.     {  
  64.         ;int * j = (int *) malloc(sizeof(int));  
  65.         *j = i;  
  66.         if(pthread_create(&ctid[i],NULL,consumer,j) != 0)  
  67.         {  
  68.             perror("create consumer failed\n");  
  69.             exit(1);  
  70.         }  
  71.     }  
  72.     for(i = 0; i < N_PRODUCER; i++)  
  73.     {  
  74.         int * j = (int *) malloc(sizeof(int));  
  75.         *j = i;  
  76.         if(pthread_create(&ptid[i],NULL,producer,j) != 0)  
  77.         {  
  78.             perror("create producer failed\n");  
  79.             exit(1);  
  80.         }  
  81.     }  
  82.     while(1)  
  83.     {  
  84.         sleep(10);  
  85.     }  
  86.     return 0;  
  87. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值