生产者与消费者模型:餐厅吃饭问题

14天阅读挑战赛


目录

一、算法简介

二、所需接口

1.头文件

2.接口简介

2.1线程控制

2.2互斥锁

2.3条件变量

三、实现流程

1.顾客流程

2.厨师流程

3.注意事项★

四、代码实现


一、算法简介

        利用多线程实现生产者消费者模型,模拟实现餐厅吃饭问题。

        顾客作为消费者,负责吃饭;厨师作为生产者,负责做饭。

二、所需接口

1.头文件

#include<pthread.h>

2.接口简介

2.1线程控制

(1)线程创建:

int pthread_create(pthread_t *tid, pthread_arr_t *arr, void* (*thread_routine)(void *), void *arg);

        pthread_t *tid:传入pthread_t类型变量的地址,获取线程id;

        pthread_arr_t *arr:线程属性,通常置为NULL;

        void* (*thread_routine)(void *):函数地址,线程的入口函数

        void *arg:向线程入口函数传递的参数。

        返回值:成功,返回0;失败,返回非0。

tid相关知识:

        每个线程都会有自己相对独立的一块空间作为自己的局部存储。

        创建线程时,返回的tid的值就是这块空间的首地址,所以通过tid就能找到这块空间,进而访问其中的数据实现对线程的控制操作。

(2)线程终止:

线程终止:退出线程

1)在线程入口函数中return

        线程的入口函数运行完毕,对应线程就会退出。

2)在任意位置调用pthread_exit函数

void pthread_exit(void *retval);

        retval:线程的退出返回值

3)在任意位置调用pthread_cancel函数

int pthread_cancel(pthread_t tid);

        tid:想要退出的线程的tid

返回值:

        如果一个线程是被取消的,则这个线程的返回值是:PTHREAD_CANCELED

功能:取消指定的线程

(3)线程等待:

int pthread_join(pthread_t tid, void **retval);

        tid:要等待的线程tid;

        void **retval:用于获取线程退出返回值

返回值:

        成功,返回0;失败,返回错误编号(非0)

2.2互斥锁

定义互斥锁:

        pthread_mutex_t;

通过接口初始化:

        pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *);

                属性通常置为NULL;

阻塞加锁:

        int pthread_mutex_lock(pthread_mutex_t *mutex);

解锁:

        int pthread_mutex_unlock(pthread_mutex_t *mutex);

释放锁资源:

        int pthread_mutex_destroy(pthread_mutex_t *mutex);

2.3条件变量

定义条件变量:

        pthread_cond_t;

初始化条件变量:

        int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);

阻塞接口:

        int pthread_cond_wait( int pthread_cond_t *cond, pthread_mutex_t *mutex);

        此接口集合了三个操作:解锁、休眠,被唤醒后加锁

唤醒接口:

        int pthread_cond_signal(pthread_cond_t cond);

        唤醒至少一个被阻塞的线程。

        int pthread_cond_broadcast(pthread_cond_t *cond);

        唤醒所有被阻塞的线程。

释放条件变量资源:

        int pthread_cond_destroy(pthread_cond_t *cond);

三、实现流程

1.顾客流程

        1)加锁;

        2)判断当前是否有饭可吃

                有,则吃饭;没有,则等待。等待前需要先解锁;被唤醒后,加锁。

        3)吃饭;

        4)解锁;

        5)唤醒厨师。

2.厨师流程

        1)加锁;

        2)判断当前是否需要做饭

                需要,则做饭;不需要,则等待。等待前需要先解锁;被唤醒后,加锁。

        3)做饭;

        4)解锁;

        5)唤醒顾客。

3.注意事项★

在多对多的情况下:

        1)因为一个厨师可能一次唤醒了多个顾客,造成多个顾客抢锁,但是只有一个顾客会抢锁成功,其他顾客则会阻塞在锁上;抢锁成功的顾客在吃完饭后解锁,时间片轮转,下一次抢到锁的则可能不是厨师,而是刚刚阻塞在锁上的顾客,则会出现“假”吃饭现象,同理也可能出现“假”做饭现象。

        解决方案:任意角色在每次加锁后,都应该重新循环上去,再次对资源进行判断是否满足获取条件。满足,则获取;不满足,则重新陷入休眠。

        2)顾客和厨师在不满足条件时,都会阻塞,加入阻塞队列等待被唤醒。但是存在一种可能:一个顾客吃完饭后,唤醒的不是厨师,而是顾客,这时两个顾客就会因为没有饭而重新陷入阻塞,从而导致程序卡死。

        解决方案:多种角色线程,则使用多个条件变量,创建多个pcb等待队列。分开等待、分开唤醒,防止错误的角色唤醒。

四、代码实现

#include<stdio.h>        
#include<pthread.h>

int dish = 0;
pthread_mutex_t mutex;
pthread_cond_t cond_cus;
pthread_cond_t cond_chef;

void *customer(void *arg) {//顾客执行流
  while (1) {
    //1.加锁
    pthread_mutex_lock(&mutex);
    while (dish == 0) {//没有饭菜,则阻塞等待
      pthread_cond_wait(&cond_cus, &mutex);
    }
    //2.吃饭菜,然后解锁、唤醒厨师
    dish = 0;
    printf("吃到青椒肉丝盖饭!没吃饱,再来一份!\n");
    pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond_chef);//唤醒厨师
  }
}

void *chef(void *arg) {
  while (1) {
    //1.加锁
    pthread_mutex_lock(&mutex);
    while (dish == 1) {//多对多时,这里必须用while判断,不能用if
      pthread_cond_wait(&cond_chef, &mutex);
    }
    //2.做饭菜,然后解锁、唤醒顾客
    dish = 1;
    printf("青椒肉丝盖饭好了!\n");
    pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond_cus);//唤醒顾客
  }
}

int main() {
  int ret;
  pthread_t customer_tid[4], chef_tid[4];//顾客线程&厨师线程

  //初始化互斥锁和条件变量
  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond_cus, NULL);
  pthread_cond_init(&cond_chef, NULL);

  //创建顾客线程&厨师线程
  for (int i = 0; i < 4; ++i) {
    ret = pthread_create(&customer_tid[i], NULL, customer, NULL);
    if (ret != 0) {
      printf("Create customer thread error!\n");
      return -1;
    }

  }
  for (int i = 0; i < 4; ++i) {
    ret = pthread_create(&chef_tid[i], NULL, chef, NULL);
    if (ret != 0) {
      printf("Create chef thread error!\n");
      return -1;
    }
  }

  //线程等待
  for (int i = 0; i < 4; ++i) {
    pthread_join(customer_tid[i], NULL);
    pthread_join(chef_tid[i], NULL);

  }

  //销毁资源
  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&cond_cus);
  pthread_cond_destroy(&cond_chef);
  return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hey小孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值