【操作系统】生产者消费者问题实现

news2024/12/23 8:12:23

目录

实验原理:

实验内容:

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

实验步骤:

实验数据及结果分析:


  • 实验原理:

考虑n个缓冲区的缓冲池作为一个共享资源,当生产者进程从数据源—文件中读取数据后将会申请一个缓冲区,并将此数据放入缓冲区中。消费者从一个缓冲区中取走数据,并将其中的内容打印输出。当生产者进程正在访问缓冲区时,消费者进程不能同时访问缓冲区,因此缓冲区是个互斥资源。

生产者、消费者程序执行流程如下图所示:

            

工程实践证明,利用信号量方法实现进程互斥是高效的,一直被广泛采用,通过信号量机制实现生产者、消费者问题,可以通过以下步骤实现。

步骤1:分配具有n个缓冲区的缓冲池,作为共享资源。

步骤2:定义两个资源型信号量empty 和full,empty信号量表示当前空的缓冲区数量,full表示当前满的缓冲区数量。

步骤3:定义互斥信号量mutex,当某个进程访问缓冲区之前先获取此信号量,在对缓冲区的操作完成后再释放此互斥信号量。以此实现多个进程对共享资源的互斥访问。

步骤4:创建3进程(或者线程)作为生产者,4个进程(或者线程)作为消费者。创建一个文件作为数据源,文件中事先写入一些内容作为内容。

步骤5 :编写代码实现生产者进程的工作内容,即从文件中读取数据,然后申请一个empty信号量,和互斥信号量,然后进入临界区操作将读取的数据放入此缓冲区中。并释放empty信号量和互斥信号量。

步骤6:编写代码实现消费者者进程的工作内容,即先申请一个full信号量,和互斥信号量,然后进入临界区操作从缓冲区中读取数据并打印输出。

Linux系统可以采用的信号量程序接口包括:

1、POSIX命名信号量:使用POSIX IPC名字标识,可用于进程或者线程间的同步。

2、POSIX内存信号量:存放在共享内存中,可用于进程或者线程间的同步。

3、SYSTEM V信号量:在内核中维护,可用于进程或者线程间的同步。

  • 实验目的:
  1. 掌握进程、线程的概念,熟悉相关的控制语;
  2. 掌握进程、线程间的同步原理和方法;
  3. 掌握进程、线程间的互斥原理和方法。
  • 实验内容:

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

创建3进程(或者线程)作为生产者,4个进程(或者线程)作为消费者。创建一个文件作为数据源,文件中事先写入一些内容作为数据。

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

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

PC计算机,操作系统:Ubuntu

  • 实验步骤:

1. 使用XShell连接终端;

2. 使用vim命令打开文本编辑器进行编码:vim prog2.c\

3. 编写数据文件data.txt

4.编译程序:

   gcc prog22.c -o a -lpthread

5. 执行程序:./a

  • 实验数据及结果分析:

实验数据

类型

初始化

mutex信号量

pthread_mutex_t

1

empty_sem

sem_t

M

full_sem

Sem_t

0

生产者循环次数

int

20

消费者循环次数

int

15

通过循环消费者生产者问题,没有发现报错,即没有出现死锁,均正常执行。

实验结果:

成功实现了生产者消费者问题。  

代码分析:

消费者函数 consumer():

  1.  void *consumer()
  2.  {
  3.   consumer_id++;
  4.   int id = consumer_id;
  5.   int count = 30;
  6.   while (count)
  7.   {
  8.    count--;
  9.    sleep(1);
  10.    sem_wait(&full_sem);        //如果为空则等待
  11.    pthread_mutex_lock(&mutex); //实现互斥
  12.    out %= M;
  13.    printf("消费者%d%d号缓冲区取出一个数据:%d\n"idout, buff[out]);
  14.    buff[out] = 0;
  15.    out++;
  16.    pthread_mutex_unlock(&mutex);
  17.    sem_post(&empty_sem);
  18.   }
  19.  }

实现:等待full信号量,加锁,产品取出缓冲区,解锁操作,并在加锁不成功时进行阻塞操作,

通过out%=M实现数组内循环;

生产者函数 producer():

  1. /*生产者方法*/
  2.  void *producer()
  3.  {
  4.   producer_id++;
  5.   int id = producer_id;
  6.   int count = 40;
  7.   while (count)
  8.   {
  9.    count--;
  10.    sleep(1);
  11.    sem_wait(&empty_sem);
  12.    pthread_mutex_lock(&mutex); //实现互斥
  13.    if (fscanf(fp, "%d", &data) == EOF)
  14.    {
  15.     fseek(fp, 0, SEEK_SET);
  16.     fscanf(fp, "%d", &data);
  17.    }
  18.    in %= M;
  19.    buff[in] = data;
  20.    printf("生产者%d%d号缓冲区放入了一个数据:%d\n", id, in, buff[in]);
  21.    in++;
  22.    pthread_mutex_unlock(&mutex);
  23.    sem_post(&full_sem);
  24.   }
  25.  }

通过fscanf和fseek实现从文件中读取数据。等待empty信号量,加锁,产品放入缓冲区,解锁操作,并在加锁不成功时进行阻塞操作。

主函数main():

  1. int main()
  2.  {
  3.   pthread_t id1[producer_Num]; //声明生产者线程的ID数组
  4.   pthread_t id2[consumer_Num]; //声明消费者现车组的ID数组
  5.   int ret1[producer_Num];
  6.   int ret2[consumer_Num];
  7.   int ini1 = sem_init(&empty_sem, 0, M); //缓冲区当前有M个空位
  8.   int ini2 = sem_init(&full_sem, 00);  //缓冲区当前有0个可用
  9.   int int3 = pthread_mutex_init(&mutex, NULL);
  10.   fp = fopen("./data.txt""r"); //打开当前目录下data.txt文件,只读
  11.   if (fp == NULL)
  12.   {
  13.    exit(1); //异常
  14.   }
  15.   //创建生产者线程
  16.   for (int i = 0; i < producer_Num; i++)
  17.   {
  18.    ret1[i] = pthread_create(&id1[i], NULL, producer, NULL);
  19.   }
  20.   //创建消费者线程
  21.   for (int i = 0; i < consumer_Num; i++)
  22.   {
  23.    ret2[i] = pthread_create(&id2[i], NULL, consumer, NULL);
  24.   }
  25.   //销毁线程
  26.   for (int i = 0; i < producer_Num; i++)
  27.   {
  28.    pthread_join(id1[i], NULL);
  29.   }
  30.   //创建消费者线程
  31.   for (int i = 0; i < consumer_Num; i++)
  32.   {
  33.    pthread_join(id2[i], NULL);
  34.   }
  35.   exit(0);
  36.  }

主函数实现了信号量的初始化以及打开文件操作,并创建生产者消费者任务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/670870.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高级数据结构——二叉搜索树

目录 1. 二叉搜索树的概念 2. 二叉搜索树的实现 结点类 二叉搜索树的类 2.1 默认成员函数 2.1.1 构造函数 2.1.2 拷贝构造函数 2.1.3 赋值运算符重载函数 2.1.4 析构函数 2.2 中序遍历 2.3 insert插入函数 2.3.1 非递归实现 2.3.2 递归实现 2.4 erase删除函数 2…

App Inventor 2 语音交互机器人Robot,使用讯飞语音识别引擎

应用介绍 App Inventor 2 语音识别及交互App。识别语言指令并控制机器人运动&#xff0c;主要用到语音识别器及文本朗读器组件&#xff0c;语音识别相关开发最佳入门。代码逻辑简单&#xff0c;App交互性及趣味性非常强~ 视频预览 语音Robot教程&#xff08;难度系数&#xf…

中科院、中科大团队精确测量子引力对量子自旋的影响

光子盒研究院 由中国科学院盛东教授、陆征天教授和中国科学技术大学的合作研究小组利用高精度氙气同位素磁力仪研究了中子自旋和引力之间的耦合效应。5月15日&#xff0c;这项题为Search for Spin-Dependent Gravitational Interactions at Earth Range的研究发表在《物理评论快…

three.js常用几何体介绍以及自定义几何体

一、自定义三角形几何体 核心代码&#xff1a; // 添加物体 // 创建几何体 for (let i 0; i < 50; i) {// 每一个三角形&#xff0c;需要3个顶点&#xff0c;每个顶点需要3个值const geometry new THREE.BufferGeometry();const positionArray new Float32Array(9);for …

Java创建多线程的五种写法

目录 一.lambda表达式(强烈推荐,最简单) 基础格式 举例 运行结果 二.继承 Thread, 重写 run 基础格式 举例 运行结果 三.实现 Runnable, 重写 run 基础格式 举例 运行结果 四.使用匿名内部类,继承 Thread, 重写 run 基础格式 举例 运行结果 五.使用匿名内部类,实…

locust学习教程(8) - event 事件

目录 1、对请求的测试前置、后置处理 2、在web界面添加新内容 3、监听测试的失败率或阀值 4、汇总总结 &#x1f381;更多干货 1、对请求的测试前置、后置处理 请求有一个上下文参数&#xff0c;通过数据有关的请求&#xff08;之类的用户名&#xff0c;标签等&#xff09…

Leaflet实现要素点击查询弹窗展示属性

leaflet是一个非常轻量的webgis框架,同时呢代码结构也比较简单。 如果项目上有需求需要大家实现对于个行政区点击查询相关属性并且展示,就像下图这样: 我们可以这样做。首先要清楚leaflet框架的构造,leaflet在加载图层的时候是对图层添加了事件监听的,也就是说用户对于图…

C++基础强化项目-职工管理系统

通过本项目练习c的基础知识 项目界面头文件workermanager.h&#xff08;管理类&#xff09;worker.h&#xff08;职工抽象类&#xff09;manager.h&#xff08;经理类&#xff09;employee.h&#xff08;普通职工类&#xff09;boss.h&#xff08;老板类&#xff09; 源文件emp…

多传感器时频信号处理:多通道非平稳数据的分析工具(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

新浪微博“私信留言收费”:私域引流危险了

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 从今日起很多新浪微博用户发现&#xff1a;微博用私信要收费了&#xff0c;确切的说是对方没有回关或回复你之前&#xff0c;你只能发送一条消息。开通会员后能发送更多留言。如下图所示&#xff1…

卷积计算加速方法--slice卷积

文章目录 1、前言2、分块卷积存在的问题3、分块卷积问题的解决方案--slice卷积4、slice卷积每层所需切分尺寸计算4、结论及加速效果 1、前言 我们在上一篇卷积计算加速方法中讨论过&#xff0c;当卷积的输入太大导致内存不够用时&#xff0c;考虑将一大块卷积分成多个小块分别进…

多快好省!硫元素循环分析内容又升级啦!

元素循环是生物地球化学循环的重要环节&#xff0c;主要涉及碳、氮、磷、硫等元素的循环过程。凌恩生物强势推出基于宏基因组的硫循环研究方案&#xff0c;构建了完整的硫循环循环模式图&#xff0c;对宏基因组数据进行深入挖掘&#xff0c;各部分结果图可直接用于文章发表&…

iOS 开发 | 自定义不规则 label

把我之前发布在简书的博客搬运过来。 目录 场景思路具体实现1. 自定义一个继承自UILabel的IrregularLabel2. 在初始化方法中进行相应初始化和设置3. 在layoutSubviews方法中进行路径的设置 最终效果箭头 label 场景 最近 App 改版&#xff0c;以下是截取的部分 UI 设计图&…

报表测试如何做?软件测试实战,超详细测试点分析(全覆盖)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 报表测试是一项重…

qt学习 tcp实现 c++

这里写目录标题 qt网络编程qt下的TCP 编程设计ui界面监听关闭和发送调试 查找网络调试助手&#xff0c;用助手当客户端测试 编写的服务端newConnection()newConnection_Slot() Tcp Client界面设计代码部分1关闭客户端发送客户端整体疑惑 https://www.bilibili.com/video/BV1tp4…

Android CMake

首先了解几个名词 NDK The Android Native Development Kit The Android NDK is a toolset that lets you implement parts of your app in native code, using languages such as C and C. For certain types of apps, this can help you reuse code libraries written in t…

虚实相生的元宇宙,不仅仅是在做虚拟社交?

互联网迭代速度已经超出了人们的想象&#xff0c;从Web1.0到Web 2.0&#xff0c;以及紧随其后的 Web 3.0。 不管我们愿不愿意承认&#xff0c;元宇宙的时代已经真真切切地到来了&#xff0c;它的兴起也是社会发展到一定阶段的必然现象。随着时代的发展&#xff0c;如今创作者的…

OpenWrt uci网络配置详解

配置文件 OpenWrt所有配置保存在/etc/config目录&#xff0c;以下为主要的网络配置文件 网络接口配置 /etc/config/network网络服务配置 /etc/config/dhcp防火墙配置 /etc/config/firewall 网络接口 OpenWrt网络接口一般包含lan口和wan口&#xff0c;但如果是X86等设备&…

十五.EtherCAT开发之对象字典的映射原理

十五.EtherCAT开发之对象字典的映射原理 15.1 协议栈文件含义 仔细阅读四个代码文件 l 文件el9800appl.c&#xff1a;主函数&#xff0c;数据收发函数所在 l 文件el9800appl.h&#xff1a;对象字典定义所在&#xff0c;包含对象字典的类型、权限、长度、映射关系、链接变量…

Restful风格笔记

Restful风格知识点 RestController注解 在类上添加RestController可以默认类中的所有方法都带有ResponseBody注解&#xff0c;可以省去一个个添加的麻烦。 RestController RequestMapping("/restful") //CrossOrigin(origins {"http://localhost:8080"…