生产者消费者&哲学家就餐解析

互斥量

int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
//亦可 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock (pthread_mutex_t* mutex);
int pthread_mutex_unlock (pthread_mutex_t* mutex);
int pthread_mutex_destroy (pthread_mutex_t* mutex);

  1. 互斥量被初始化为非锁定状态;
  2. 线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
  3. 线程2调用pthread_mutex_lock函数,阻塞等待;
  4. 线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
  5. 线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态;
条件变量
条件变量可以让调用线程在满足特定条件的情况下暂停。

int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
mutexint pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);使调用线程睡入条件变量cond,同时释放互斥锁
int pthread_cond_timedwait (pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime);
int pthread_cond_signal (pthread_cond_t* cond); 从条件变量cond中唤出一个线程,令其重新获得原先的互斥锁
注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。
int pthread_cond_broadcast (pthread_cond_t* cond);从条件变量cond中唤出所有线程
int pthread_cond_destroy (pthread_cond_t* cond);

生产者消费者

生产者:产生数据的线程。
消费者:使用数据的线程。

在这里插入图片描述
通过缓冲区隔离生产者和消费者,与二者直连相比,避免相互等待,提高运行效率。
生产快于消费,缓冲区满,撑死。
消费快于生产,缓冲区空,饿死。

  1 #include<stdio.h>
  2 #include<pthread.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 #include<time.h>
  6 #include<string.h>
  7 #include<error.h>
  8 //定义一个仓库结构体
  9 typedef struct StoreHouse{
 10     int cnt;//仓库里的数量
 11     char store[10];//容量
 12 }StoreHouse;
 13 //仓库 共享资源
 14 StoreHouse s = {};
 15 
 16 pthread_mutex_t mutex;//互斥量
 17 pthread_cond_t notfull;//条件变量
 18 pthread_cond_t notempty;
 19 
 20 void *producer(void *arg){
 21     while(1){//一直生产
 22         pthread_mutex_lock(&mutex);//上锁
 23         while(s.cnt >= 10){//注意此处用while
 24             //使调用线程睡入条件变量cond,同时释放互斥锁mutex
 25             pthread_cond_wait(&notfull,&mutex);
 26         }
 27         char p = 'A'+rand()%26;//生产一些字母
 28         s.store[s.cnt++] = p;
 29         int i;
 30         for(i=0;i<s.cnt;i++){//打印一下
 31             printf("%c ",s.store[i]);
 32         }
 33         printf("<----%c\n",p);//打印生产的字母
 34         //从条件变量cond中唤出一个线程,令其重新获得原先的互斥锁
 35         //被唤出的线程此刻将从pthread_cond_wait函数中返回,但如果该线程无法>    获得原先的锁,则会继续阻塞在加锁上。
 36         pthread_mutex_unlock(&mutex);//解锁
 37         pthread_cond_signal(&notempty);
 38         usleep(rand()%100000);//睡一会
 39     }
 40     return NULL;
 41 }
 42 
 43 void *consumer(void *arg){
 44     while(1){//一直消费
 45         pthread_mutex_lock(&mutex);
 46         while(s.cnt <= 0){
 47             pthread_cond_wait(&notempty,&mutex);
 48         }
 49         char c = s.store[--s.cnt];
 50         int i;
 51         for(i=0;i<s.cnt;i++){
 52             printf("%c ",s.store[i]);
 53         }
 54         printf("---->%c\n",c);
 55         pthread_mutex_unlock(&mutex);
 56         pthread_cond_signal(&notfull);
 57         usleep(rand()%100000);
 58     }
 59     return NULL;
 60 }
 61 int main(){
 62     srand(time(NULL));//设置随机种子
 63     pthread_mutex_init(&mutex,NULL);//初始化
 64     pthread_cond_init(&notfull,NULL);
 65     pthread_cond_init(&notempty,NULL);
 66     //创建6个线程,3个生产,3个消费
 67     pthread_t ids[6];//线程ID
 68     int i;
 69     for(i=0;i<6;i++){
 70         if(i%2 == 0){
 71             int produce = pthread_create(&ids[i],NULL,producer,NULL);
 72             if(produce != 0){//线程创建成功返回0,失败返回-1,注意不能用perr    or,线程有自己的错误码
 73                 printf("produce:%s\n",strerror(produce));
 74                 return -1;
 75             }
 76         }else{
 77             int consume = pthread_create(&ids[i],NULL,consumer,NULL);
 78             if(consume != 0){
 79                 printf("consumer:%s\n",strerror(consume));
 80                 return -1;
 81             }
 82         }
 83     }
 84     for(i=0;i<6;i++){
 85         //用来等待线程的结束,线程见同步的操作
 86         int join = pthread_join(ids[i],NULL);
 87         if(join != 0){
 88             printf("join:%s\n",strerror(join));
 89             return -1;
 90         }
 91     }
 92     //销毁
 93     pthread_mutex_destroy(&mutex);
 94     pthread_cond_destroy(&notfull);
 95     pthread_cond_destroy(&notempty);
 96     return 0;
 97 }

1.gcc编译时,加上-lphread选项,例:gcc eating.c -lpthread
2.usleep是毫秒级的阻塞
在这里插入图片描述

哲学家就餐问题

1965年,著名计算机科学家艾兹格·迪科斯彻,提出并解决了一个他称之为哲学家就餐的同步问题。
从那时起,每个发明同步原语的人,都希望通过解决哲学家就餐问题来展示其同步原语的精妙之处。
这个问题可以简单地描述如下:
五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。
相邻两个盘子之间放有一把叉子。哲学家的生活中有两种交替活动时段:即吃饭和思考。
当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。
如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。

在这里插入图片描述

  1 #include<stdio.h>
  2 #include<pthread.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 #include<time.h>
  6 #include<string.h>
  7 #include<error.h>
  8 //初始化互斥量
  9 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 10 //初始化条件变量
 11 pthread_cond_t cond[5] = {
 12     PTHREAD_COND_INITIALIZER,
 13     PTHREAD_COND_INITIALIZER,
 14     PTHREAD_COND_INITIALIZER,
 15     PTHREAD_COND_INITIALIZER,
 16     PTHREAD_COND_INITIALIZER,
 17 };
 18 char *name[5] = {"黄忠","关羽","刘备","张飞","马超"};
 19 //定义3种状态,分别为吃饭,挨饿,思考
 20 typedef enum State{
 21     EATING,
 22     HUNGRING,
 23     THINKING
 24 }State;
 25 //一开始的状态都为思考
 26 State state[5] = {
 27     THINKING,
 28     THINKING,
 29     THINKING,
 30     THINKING,
 31     THINKING,
 32 };
 33 void *dinner(void *arg){//餐桌上
 34     int i = (int)arg;
 35     while(1){//一直循环吃和思考
 36         eat(i);
 37         think(i);
 38     }
 39 }
 40 int eat(int i){
 41     int l = (i-1+5)%5;//当前哲学家左边的人
 42     int r = (i+1)%5;//当前哲学家右边的人
 43     pthread_mutex_lock(&mutex);//上锁
 44     while(state[l] == EATING || state[r] == EATING){//左边右边有一个在吃就等着
 45         printf("%s正在挨饿\n",name[i]);
 46         state[i] = HUNGRING;//状态变为挨饿
 47         pthread_cond_wait(&cond[i],&mutex);
 48     }
 49     printf("%s开始吃饭了\n",name[i]);
 50     state[i] = EATING;
 51     pthread_mutex_unlock(&mutex);//解锁
 52     usleep(rand()%100000);
 53 }
 54 int think(int i){
 55     int l = (i-1+5)%5;
 56     int r = (i+1)%5;
 57     pthread_mutex_lock(&mutex);//上锁
 58     printf("%s吃饱了开始思考如何打仗了\n",name[i]);
 59     state[i] = THINKING;//修改状态为思考
 60     pthread_mutex_unlock(&mutex);//解锁
 61     pthread_cond_signal(&cond[l]);//给左边发信号去唤醒
 62     pthread_cond_signal(&cond[r]);//给右边发信号去唤醒
 63     usleep(rand()%100000);
 64 }
 65 int main(){
 66     srand(time(NULL));
 67     pthread_t ids[5];
 68     int i;
 69     for(i=0;i<5;i++){
 70         int ret = pthread_create(&ids[i],NULL,dinner,(void *)i);
 71         if(ret != 0){
 72             printf("pthread_create:%s\n",strerror(ret));
 73             return -1;
 74         }
 75     }
 76     for(i=0;i<5;i++){
 77         int join = pthread_join(ids[i],NULL);
 78         if(join != 0){
 79             printf("join:%s\n",strerror(join));
 80             return -1;
 81         }
 82     }
 83     return 0;
 84 }

在这里插入图片描述
pthread_create的第4个参数,一般为(void*)(&参数) 这样,函数中转换类型为* (参数类型 *)arg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值