swjtu操作系统实验报告3:生产者消费者问题

实验目的

在Linux上使用C语言编写一个多线程程序,并通过信号量机制来模拟解决一个有界缓冲区的生产者—消费者问题。其中一个线程代表生产者,另外一个线程代表消费者,使得生产者和消费者线程可以周而复始的工作而不会出现错误。通过这个过程来了解Linux多线程的工作原理。

思路:

注:首先需要在codeblocks的project-build options-linker seetings中添加(add)pthread库。

实验代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>   // 实现多线程的头文件
#include <semaphore.h> // 实现信号量定义的头文件

#define producerNumber 3   // 生产者的数目
#define consumerNumber 5   // 消费者的数目
#define M 5 // 缓冲区数目

int in = 0;   // 生产者放置产品的位置
int out = 0; // 消费者取产品的位置

int buff[M] = {0}; // 缓冲初始化为0, 开始时没有产品
//信号量的数据类型为结构sem_t,它本质上是一个长整型的数
sem_t empty_sem;  //同步信号量,还有没有空位可以放进去
sem_t full_sem;   // 同步信号量,有没有填好的可以消费
pthread_mutex_t mutex; // 互斥信号量

int producer_id = 0;   // 生产者id
int consumer_id = 0; // 消费者id

/* 打印缓冲情况 */
void print()
{
for(int i = 0; i < M; i++)
   printf("%d ", buff[i]);
printf("\n");
}

/* 生产者方法 */
void *product()
{
int id = ++producer_id;

while(1)
{
   // 用sleep的数量可以调节生产和消费的速度,便于观察
   sleep(1);

   sem_wait(&empty_sem); // 满不放;
//int sem_wait(sem_t *sem), 以原子操作的方式将信号量的值减1  
   pthread_mutex_lock(&mutex); //互斥,在成功完成返回零,否则错误

   in = in % M;
   buff[in] = rand()%10;
   printf("生产者%d向%d号缓冲区放入了数据%d: \t", id, in, buff[in]);
   print();
   ++in;

   pthread_mutex_unlock(&mutex);// 解除锁定 mutex 所指向的互斥锁
   sem_post(&full_sem);
// int sem_post(sem_t *sem) 以原子操作方式将信号量的值加1,调用成功时返回0,失败//返回-1 
}
}

/* 消费者方法 */
void *prochase()
{
int id = ++consumer_id;
while(1)
{
   sleep(1);// 用sleep的数量可以调节生产和消费的速度,便于观察
   sem_wait(&full_sem); // 空不取
   pthread_mutex_lock(&mutex);
   out = out % M;
   printf("消费者%d从%d号缓冲区取出了数据%d: \t", id, out, buff[out]);
   buff[out] = 0;
   print();
   ++out;
   pthread_mutex_unlock(&mutex);
   sem_post(&empty_sem);
}
}

int main()
{
pthread_t id1[producerNumber]; // 声明生产者线程的ID数组
pthread_t id2[consumerNumber]; // 声明消费者线程的ID数组
int i;
int ret1[producerNumber];int ret2[consumerNumber];

int ini1 = sem_init(&empty_sem, 0, M); // 初始化同步信号量
// int sem_init (sem_t * sem, int pshared, unsigned int value);
// sem为指向信号量结构的一个指针;pshared==0确保只能为当前的进程的所有线程共享,//否则多个进程共享,value为信号量的初始值. 调用成功时返回0。
int ini2 = sem_init(&full_sem, 0, 0);
if(ini1 && ini2 != 0) //初始化失败
{
   printf("sem init failed \n");
   exit(1);
}

//初始化互斥信号量的函数pthread_mutex_init();
int ini3 = pthread_mutex_init(&mutex, NULL);
//int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); 以动态方式创建互斥锁
//参数1:为指向互斥信号量结构的一个指针,参数2: 指定了新建互斥锁的属性,NULL表明使用默认的互斥锁属性(快速互斥锁)
if(ini3 != 0)
{
   printf("mutex init failed \n");
   exit(1);
}
// 创建producerNumber个生产者线程
for(i = 0; i < producerNumber; i++)
{
   ret1[i] = pthread_create(&id1[i], NULL, product, NULL);
   // 用ret1[]数组记录是否创建线程成功,若返回值为0表示创建成功,否则返回出错编号
//int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
   //参数1为指向线程标识符(ID号)的指针,参数2用来设置线程的属性,参数3为线程运行函数的地址,参数4为运行函数的参数
   if(ret1[i] != 0)
   {
    printf("product%d creation failed \n", i);
    exit(1);
   }
}
//创建consumerNumber个消费者线程
for(i = 0; i < consumerNumber; i++)
{
   ret2[i] = pthread_create(&id2[i], NULL, prochase, NULL);
   if(ret2[i] != 0)
   {
    printf("prochase%d creation failed \n", i);
    exit(1);
   }
}
//销毁线程
for(i = 0; i < producerNumber; i++)
{
   pthread_join(id1[i],NULL);
   //pthread_join()函数来使主线程阻塞以等待其他线程(也就是刚才创建的线程)退出
   //函数定义int pthread_join(pthread_t thread, void **retval);
   //参数thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
}

for(i = 0; i < consumerNumber; i++)
{
  pthread_join(id2[i],NULL);
}
exit(0);
}

修改代码,int buff[M] = {1,2,3,4,5}, int ini1 = sem_init(&empty_sem, 0,0); int ini2 = sem_init(&full_sem, 0, M),即缓冲区一开始已经放满,运行结果为:

即生产者不会在缓冲区满时加入数据。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

guts350

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

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

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

打赏作者

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

抵扣说明:

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

余额充值