操作系统实验-生产者/消费者问题的实现

该实验旨在掌握进程和线程的概念,以及它们之间的同步和互斥原理。通过使用信号量和互斥量解决生产者/消费者问题,涉及n个缓冲区的缓冲池,生产者从文件读取数据放入缓冲区,消费者取出数据并打印。实验创建多个生产者和消费者线程,保证并发执行并遵循同步原则。代码示例展示了如何用互斥量和信号量实现这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、实验名称:生产者/消费者问题的实现

二、实验目的:

(1)掌握进程、线程的概念,熟悉相关的控制语。

(2)掌握进程、线程间的同步原理和方法。

(3)掌握进程、线程间的互斥原理和方法。

(4)掌握使用信号量原语解决进程、线程间互斥和同步方法。

(5)利用信号量、互斥量实现生产者/消费者问题。

三、实验原理:

(1)n个缓冲区的缓冲池作为一个临界资源:当生产者任务从数据源—文件中读取数据后将会申请一个缓冲区,并将此数据放入缓冲区中。消费者任务从一个缓冲区中取走数据,并将其中的内容打印输出。

(2)当一个生产者任务正在访问缓冲区时,其他生产者和消费者任务不能访问缓冲区;当一个消费者任务正在访问缓冲区时,其他其他生产者和消费者任务不能访问缓冲区;使用互斥量实现对缓冲池的互斥访问。

(3)生产者任务在向缓冲池放入数据之前需要判断缓冲池中是否还有空的缓冲区,如果有则向空的缓冲区写入,如果没有则等待;消费者任务在从缓冲池读取数据之前需要判断缓冲池中是否有已经写入数据的缓冲区,如果有则读取已经写入数据的缓冲区,如果没有则等待;使用两个信号量:一个代表空缓冲区的数量,一个代表已经写入数据的缓冲区数量。

四、实验内容:

(1)有一群生产者任务在生产产品,并将这些产品提供给消费者任务去消费。为使生产者任务与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池:生产者任务从文件中读取一个数据,并将它存放到一个缓冲区中;消费者任务从一个缓冲区中取走数据,并输出此数据(printf);生产者和消费者之间必须保持同步原则:不允许消费者任务到一个空缓冲区去取产品,也不允许生产者任务向一个已装满产品且尚未被取走的缓冲区中投放产品。

(2)创建3个进程(或者线程)作为生产者任务,4个进程(或者线程)作为消费者任务

(3)创建一个文件作为数据源,文件中事先写入一些内容作为数据(字符串或者数值)

(4)生产者和消费者任务(进程或者线程)都具有相同的优先级

五、实验器材(设备、元器件):

  1. 学生每人一台PC,安装Windows8操作系统。
  2. 个人PC安装VMware虚拟机和Ubuntu系统。

六、实验步骤:

(1)在Ubuntu系统中使用“Ctrl+Alt+T”打开终端;

(2)使用vim编写数据文件data.txt

(3)使用“vim lab2.c”命令打开vim文本编辑器 ,并编写lab2.c源程序实现生产者/消费者问题

(4)编译程序:gcc lab2.c -o test

(5)执行程序:./test

七、代码实现

        为解决生产者/消费者问题中的同步与互斥问题,使用互斥量mutex来实现对缓冲区的互斥访问,使用信号量full、empty分别代表满缓冲区的数量以及空缓冲区的数量来实现生产者与消费者的同步问题,代码如下所示:

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<stdlib.h>
#include<unistd.h>
#define N 5 //定义缓冲区长度
pthread_mutex_t mutex; // 声明互斥量
sem_t full,empty; // 声明两个信号量
void init();
void destroy();
void *consumer(void *);
void *producer(void *);
char buffer[N];
int in = 0,out = 0;
FILE* fp;

int main(){
    init(); // 初始化互斥量跟信号量
    pthread_t p[3],c[4];
    char* id[] = {"1","2","3","4"};
    fp = fopen("/home/qinxinpeng/data.txt","r");
    if(fp == NULL){
        printf("打开文件失败!");
        return 0;
    }
    int i;
    for(i = 0; i < 3; i++){
        pthread_create(&p[i],NULL,producer,(void *)id[i]);
    }
    for(i = 0; i < 4; i++){
        pthread_create(&c[i],NULL,consumer,(void *)id[i]);
    }
    for(i = 0; i < 3; i++){
        pthread_join(p[i],NULL);
    }
    for(i = 0; i < 4; i++){
        pthread_join(c[i],NULL);
    }
    destroy();
    fclose(fp);
    return 0;
}
// 消费者函数
void *consumer(void *arg){
    int id = atoi((char*)arg);
    int count = 6;
    while(count > 0){
        char nextch;
        sem_wait(&full);
        pthread_mutex_lock(&mutex);
        nextch = buffer[out];
        out = (out + 1) % N;
        printf("消费者%d从缓冲区取走数据%c\n",id,nextch);
        pthread_mutex_unlock(&mutex);
        sem_post(&empty);
        sleep(1);
        count--;
    }
}
// 生产者函数
void *producer(void *arg){
    int id = atoi((char*)arg);
    int count = 8;
    while(count > 0){
        char nextch = fgetc(fp);// 生产字符
        sem_wait(&empty);
        pthread_mutex_lock(&mutex);
        buffer[in] = nextch;
        in = (in + 1) % N;
        printf("生产者%d向缓冲区添加数据%c\n",id,nextch);
        pthread_mutex_unlock(&mutex);
        sem_post(&full);
        sleep(1);
        count--;
    }
}

void init(){
    mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
    sem_init(&full,0,0);
    sem_init(&empty,0,N);
    int i;
    for(i = 0; i < N; i++){
        buffer[i] = '\0';
    }
}

void destroy(){
    sem_destroy(&full);
    sem_destroy(&empty);
}

八、总结及心得体会

        通过本次实验,进一步掌握了进程、线程的概念以及两者之间的同步原理和方法、互斥原理和方法,熟悉了相关的原语。同时也掌握了使用信号量原语解决进程、线程间互斥和同步方法,并能够利用信号量、互斥量实现生产者/消费者问题。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

实名吃香菜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值