经典进程同步问题(一)——生产者消费者问题

2023-05-16

目录

一、生产者消费者问题描述

二、解决思路

三、问题求解:

四、源码

五、运行结果: 


一、生产者消费者问题描述

        生产者消费者(producer—customer)问题是一个非常著名的进程同步问题。它描述的是:生产者进程在生产“产品”,消费者进程则消费这些“产品”,在生产者进程和消费者进程之间存在一个缓冲池(生产者将生产的产品放入该缓冲池,消费者则消费该缓冲池中的产品)。所有生产者进程、消费者进程都是相互独立的(即以异步的方式运行),但他们之间同时必须保持同步(即不允许消费者进程到空缓冲区取产品,也不允许生产者进程向满缓冲区存入产品)。

二、解决思路

        我们使用队列(先进先出表)Queue来表示上述具有n个缓冲区的缓冲池。每投入(取出)一个产品,缓存池中就相应的插入(删除)一个节点。由于该缓冲池是被组织成队列的形式,因此,队空队满的判断条件分别如下:

q->front == q->rear;    //队空
q->front == (q->rear+1)%SIZE;    //队满

        此外,我们引入一个整型变量num,置其初值为0,当生产者(消费者)进程向缓冲池投入(取走)一个产品时,num对应的加一(减一)。

        同时,由于缓冲区是共享的,因此需要对生产者、消费者使用缓冲区进行限制,以此达到同步的效果,即在生产者向缓冲区投入产品时,消费者不得使用缓冲池;消费者向缓冲区取出产品时同理。

三、问题求解

通过以上的分析,我们可以得到一个解决思路如下:

void producer()
{
    sem_wait();
    lockf();
    QueueFull();
    Enqueue();
    unlockf();
    sem_post();
}
void customer()
{
    sem_wait();
    lockf();
    QueueEmpty();
    Dequeue();
    unlockf();
    sem_post();
}

  这段伪代码的思路已经十分明确了。

·生产者进程向缓冲区投入产品,则num加一;消费者进程向缓冲区取出产品,则num减一

·为避免生产者进程和消费者进程同时使用资源,我们采用上锁的方式来达到独占资源的目标

·为了避免两个进程之间相互干扰,我们利用信号量机制来实现进程间互斥和同步   

但是,仔细思考就会发现,仅仅只是这样是远远不够的。我们还必须保证队满不入、队空不取。因此,在上述结构的基础上,我们加入条件判断机制。

四、源码

由于容器Queue的容量为10,因此我只运行生产者、消费者各十次,且出于简单设计的角度,该程序仅仅展示了单生产者-单消费者的情况。其他的情况,如果后面有空余时间,我会尝试一下的。

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
 
#define TRUE  1
#define FALSE 0
#define SIZE 11
 
typedef int QueueData;	//定义一个整型变量QueueData,与为基本数据类型定义新的别名方法一样 

typedef struct _queue   //队列结构体
{
	int data[SIZE];
	int front;     // 指向队头的下标
	int rear;      // 指向队尾的下标
}Queue;
 
struct data             //信号量结构体
{
	sem_t count;
	Queue q;
};

struct data sem;
pthread_mutex_t mutex;	//互斥变量使用特定的数据类型 
int num = 0; 

int InitQueue (Queue *q)   // 队列初始化
{
	if (q == NULL)
	{
		return FALSE;
	}
	q->front = 0;
	q->rear  = 0;
	return TRUE;
}
 
int QueueEmpty (Queue *q)      //判断空队情况
{
	if (q == NULL)
	{
		return FALSE;
	}
	return q->front == q->rear;
}
 
int QueueFull (Queue *q)     //判断队满的情况
{
	if (q == NULL)
	{
		return FALSE;
	}
	return q->front == (q->rear+1)%SIZE;
} 
 
int DeQueue (Queue *q, int x)   //出队函数
{
	if (q == NULL)
	{
		return FALSE;
	}
	if (QueueEmpty(q))
	{
		return FALSE;
	}
	q->front = (q->front + 1) % SIZE;
	x = q->data[q->front];
	return TRUE;
}
 
int EnQueue (Queue *q, int x)   //进队函数
{
	if (q == NULL)
	{
		return FALSE;
	}	
	if (QueueFull(q))
	{
		return FALSE;
	}	
	q->rear = (q->rear+1) % SIZE;
	q->data[q->rear] = x;
	return TRUE;
}
 
void *Producer()
{
	int i=0;
	while(i<10)
	{
		i++;
		int time = rand() % 10 + 1;          //随机使程序睡眠0点几秒           
		usleep(time * 100000);
		                                      
		sem_wait(&sem.count);                 //信号量的P操作(使信号量的值减一) 
		pthread_mutex_lock(&mutex);           //互斥锁上锁
		if(!QueueFull(&sem.q))					//若队未满 
		{
			num++;                                
			EnQueue (&sem.q, num);              //消息进队
			printf("生产了一条消息,count=%d\n", num); 
		}                                      
		else	printf("Full\n");                                       
		pthread_mutex_unlock(&mutex);         //互斥锁解锁
		sem_post(&sem.count);                  //信号量的V操作(使信号量的值加一) 
	}
	printf("i(producer)=%d\n",i);
}
 
void *Customer()
{
	int i=0;
	while(i<10)
	{
		i++;
		int time = rand() % 10 + 1;   //随机使程序睡眠0点几秒
		usleep(time * 100000);
		
		sem_wait(&sem.count);           //信号量的P操作
		pthread_mutex_lock(&mutex);    //互斥锁上锁
		
		if(!QueueEmpty(&sem.q))			//若队未空 
		{
			num--;
			DeQueue (&sem.q, num);       //消息出队
			printf("消费了一条消息,count=%d\n",num);
		}
		else	printf("Empty\n");
		pthread_mutex_unlock(&mutex);  //互斥锁解锁
		sem_post(&sem.count);         //信号量的V操作
	}
	printf("i(customer)=%d\n",i);
}
 
int main()
{
	srand((unsigned int)time(NULL));
	
	//信号量地址,信号量在线程间共享,信号量的初始值 
	sem_init(&sem.count, 0, 10);    //信号量初始化(做多容纳10条消息,容纳了10条生产者将不会生产消息)
	
	pthread_mutex_init(&mutex, NULL);  //互斥锁初始化
	
	InitQueue(&(sem.q));   //队列初始化
	
	pthread_t producid;
	pthread_t consumid;
	
	pthread_create(&producid, NULL, Producer, NULL);   //创建生产者线程
	pthread_create(&consumid, NULL, Customer, NULL);   //创建消费者线程
	
	pthread_join(consumid, NULL);    //线程等待,如果没有这一步,主程序会直接结束,导致线程也直接退出。
	
	sem_destroy(&sem.count);         //信号量的销毁 
	
	pthread_mutex_destroy(&mutex);   //互斥锁的销毁
	
	return 0;
}

五、运行结果: 

仔细分析可以认为达成实验目的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

经典进程同步问题(一)——生产者消费者问题 的相关文章

  • 【玖哥乱弹】编程语言间的斗争

    在初级程序员阶段 xff0c 每个人都不可避免遇到选择编程语言和职业方向的难题 我挑选了几个常见的编程语言 xff0c 分析了优缺点和职业方向 xff0c 以供想当程序员的人参考 C C 43 43 一句话概括 xff1a 大多数中国程序员
  • 【玖哥乱弹】成功的IT人士这样转型AI

    AlphaGo在与围棋世界冠军的对弈大获全胜 xff0c 不但让我们领略到了AI的巨大潜力 xff0c 还把AI推上了新的浪潮之巅 作为一个从即将过去的移动互联网时代走来的Android工程师 xff0c 我深深感受到自己成了传统行业 xf
  • 【玖哥乱弹】程序员如何成为别人的男朋友

    这个世界上程序员数量很多 xff0c 有女朋友的程序员在其中的比例却很少 究其原因 xff0c 不外乎大多数程序员根本不知道怎么才能成为别人的男朋友 成为别人的男朋友对于富二代和拆迁户很容易 xff0c 而对于程序员却很难 xff0c 潘驴
  • wm命令使用方法(修改android 分辨率)修改

    注 xff1a Android 4 3引入的wm工具 wm命令及用法 xff1a 系统说明 xff1a usage wm subcommand options wm size reset WxH wm density reset DENSI
  • 给单个项目单独设置git账号

    一 直接复制带有git账号的项目 直接复制整体项目 其中里面带有 git文件 二 其他方案 暂时没查出来
  • 互联网 Java 工程师面试题之Spring(二)

    Spring 面试题 xff08 二 xff09 1 什么是 spring Spring 是个 java 企业级应用的开源开发框架 Spring 主要用来开发 Java 应用 xff0c 但是有些扩展是针对构建 J2EE 平台的 web 应
  • (IDEA2020 在使用maven时遇到servlet依赖包错误,Error:(6, 37) java: 程序包org.apache.ibatis.annotations不存在)

    IDEA2020 在使用maven时遇到servlet依赖包错误 xff0c Error 6 37 java 程序包org apache ibatis annotations不存在 一 错误显示 xff0c 在本地仓库有依赖的情况下 xff
  • Spring Security(十一) Spring Security 中 CSRF

    从刚开始学习 Spring Security 时 xff0c 在配置类中一直存在这样一行代码 xff1a http csrf disable 如果没有这行代码导致用户无法被认证 这行代码的含义是 xff1a 关闭 csrf 防护 1 什么是
  • 谈谈我对多线程的理解

    一 提到多线程 xff0c 就不得不理解以下几点 xff1a 1 程序 xff0c 进程 xff0c 线程这三者之间的关系 xff1f 简单来说 xff0c 一程序可以调用多个进程 xff0c 比如一个视频播放器程序 xff0c 里面就存在
  • 消费者行为分析包含了哪些内容?

    消费者市场 指个人或家庭为满足生活需求而购买或租用商品的市场 消费者市场特点 1 购买者众多 xff0c 购买数量零星 xff0c 对日用品的消费需要经常性购买 xff0c 购买频率高且量小 xff0c 支付的金额数也小 2 需求差异性大
  • 一个快速从中文文本抽取关键短语的工具 ckpe 提取关键短语

    一个从 中文自然语言文本 中抽取 关键短语 的工具 需要调用的话 xff0c 请直接进入github查看调用方法 xff1a 源码地址 xff1a ckpe 戳这里 61 gt 在线直接试用 关键短语抽取在线版 应用场景 Applicati
  • kali 下libnl-3-dev : 依赖: libnl-3-200 (= 3.2.24-2) 但是 3.2.27-2 正要被安装

    1 先说问题 sudo apt get install libnl span class hljs number 3 span dev 正在读取软件包列表 span class hljs keyword span 完成 正在分析软件包的依赖
  • android的system域解耦

    google很早在为此做准备 xff0c 要求所有设备能够刷GSI xff08 通用系统镜像 xff09 xff0c 并跑过XTS测试 动态分区解耦方案如上图 一 分区描述 单一系统映像 SSI 包含system和system ext图像的
  • Android Activity onConfigurationChanged()方法 监听状态改变

    AndroidManifest xml文件 xff1a 1 增加权限android permission CHANGE CONFIGURATION 2 设置Activity的android configChanges属性 span clas
  • 生产者消费者

    问题意义 生产者消费者问题是一个很经典的问题 xff0c 通过解决此问题 xff0c 能够学到多线程的的知识 程序设计思路 在本程序中采用信号灯的方式 xff0c 用Flag作为标志位 xff0c 指示生产过程和消费过程是否结束 Flag为
  • android keymaster

    keymaster span class hljs keyword is span a newly instroduced key management hardware abstraction layer hal component It
  • Android密钥证书管理相关介绍

    Java Security Java Security是Java中的安全模块 xff0c 它对应了一系列的规范 xff0c 主要包含三个重要规范 xff1a JavaCryptography Extension xff08 简写为JCE x
  • 安装依赖包 -- Ubuntu

    安装依赖包 xff08 须在虚拟环境中 xff09 xff1a 依赖就是开发以及程序运行需要使用的环境的集合 包括软件 插件等 我们一般会把需要使用的依赖给保存在一个文件中 xff0c 命名为requirements的txt文件 如果在其它
  • 大数据的感想

    1 大数据即全数据 xff08 即n 61 All xff0c 这里n为数据的大小 xff09 xff0c 其旨在收集和分析与某事物相关的 全部 数据 xff0c 而非仅分析 部分 数据 2 N 61 All xff08 所有 xff09
  • 新手如何配置spring

    首先 xff0c 可以通过springframework官方说明文档找到如下网址下载spring包 http repo spring io release org springframework spring 这里以4 2为例子 sprin

随机推荐

  • 对Zookeeper的分布式锁的浅解

    提起到锁 xff0c 我们在单机应用开发涉及到同步问题时往往会采用Sychronized Lock的方式进行解决多线程的代码同步问题 xff0c 此时多线程的运行都是在同一个JVM中 但是在分布式集群的情况下 xff0c 此时多线程的运行在
  • com.github.tobato.fastdfs.exception.FdfsServerException: 错误码:2,错误信息:找不到节点或文件

    我的原因是因为docker中Tracker没有启动起来 xff0c 重新启动后系统正常
  • Java线程、Java多线程详细介绍

    目录 一 进程和线程的区别 1 1 进程 1 2 线程 二 并发和并行 2 1 并行 2 2 并发 2 3 监控线程的执行情况 三 创建方式 3 1 继承Thread类 思考 xff1a 为什么不直接通过对象调用start xff08 xf
  • 抽象类和接口的区别(通俗易理解)

    目录 一 抽象类 1 1 抽象类概念 xff1a 1 2 抽象类特点 1 3 抽象类存在价值 二 接口 2 1 接口的概念 2 2 接口的特点 2 3 接口存在价值 三 接口和抽象类的关系 3 1 接口和抽象类相同点 3 2 接口和抽象类的
  • MyBatis缓存介绍

    提到MyBatis的缓存 xff0c 首先需要了解一下缓存是什么 一 缓存 1 1 什么是缓存 缓存是服务器内存的一块区域 1 2 什么样的数据适合使用缓存 经常访问但又不会时时发生变化的数据 1 3 缓存的设计目的 提高查询速度 xff0
  • 数据库的乐观锁和悲观锁

    一 悲观锁 悲观锁是一种思想 xff0c 对数据被其他事务修改持保守态度 xff0c 会通过数据库自身的锁机制来实现 xff0c 从而保证数据操作的排它性 悲观锁总是假设最坏的情况 xff0c 每次请求取数据的时候都认为请求会修改数据 xf
  • deepin实现微信双开

    说明 linux环境下进行微信双开本质是修改微信的启动脚本 xff0c 为每个微信进程指定一个工作目录 所以按照原理来讲 xff0c 你可以参考显得方法实现微信多开 操作方法 1 先备份 sudo cp opt apps com qq we
  • vue 引入字体图标显示方块

    问题现象 xff1a 使用element ui xff0c 字体图表显示 方块 项目加载后 xff0c 看加载文件 xff0c 并没有 woff或 woff2或 ttf等字体图标文件 也没有字体图表转换后的base64 xff0c 文件 环
  • mysql8.0报错解决方式:1449 - The user specified as a definer (‘root‘@‘%‘) does not exist

    今天在Navicat上新建本地数据库连接的时候 xff0c 输入正确的账号密码点击连接之后却报错 查询相关文件之后 xff0c 发现是权限问题 产生用户不能授权的原因是mysql 数据库中user 表中的特定用户 root 的host 的属
  • AD中PCB板设计中如何将正反面相互对调

    在pcb布局过程中 xff0c 有时会碰到正反面布局反了的情况 xff0c 且走线也已经大部分完成的时候 xff0c 我们可以利用AD软件的快捷键 xff0c 实现正反面相互对调的过程 按L键 xff0c 打开所有层显示 要对调的层 xff
  • 非分区表转换为分区表的三种方式

    创建测试环境 span class token comment 创建非分区表 span span class token keyword CREATE span span class token keyword TABLE span T N
  • 表管理语法

    表操作 表的创建表的修改修改列名修改列类型或约束添加新列删除列修改表名 表的删除删除表数据及结构删除表数据使用DELETE语法使用TRUNCATE语法 表的复制仅仅复制表结构复制表的结构 43 数据复制全部数据复制部分数据仅仅复制部分结构跨
  • 事务

    事务控制语言 事务的特性特点分类 事务的创建数据库隔离级别各种并发问题隔离级别 delete和truncate在事务使用时的区别 事务的特性 一条或多条SQ语句组成一个执行单位 xff0c 要么全执行 xff0c 要么全不执行 特点 A原子
  • 视图

    视图 简单介绍创建视图语法好处 修改视图语法 删除视图语法 查看视图语法 更新视图以下类型试图不可更新 视图和表比较 简单介绍 MySQL从5 0 1版本开始提供视图功能 xff0c 视图是一个虚拟表 xff0c 行和列的数据来自定义视图的
  • 存储过程和函数

    存储过程和函数 前提变量分类 系统变量使用作用域 自定义变量使用用户变量使用局部变量使用 作用域用户变量和局部变量对比 举例用户变量局部变量 存储过程和函数存储过程基本介绍语法举例 函数储过程和函数区别语法举例无参数又返回有参有返回 流程控
  • mysq简介

    mysql简介 RPM安装查看当前系统是否安装mysql安装查看mysql所属组和密码启动关闭mysql设置开机自启动安装位置修改字符集 配置文件二进制日志log bin错误日志log error查询日志log数据文件 存储引擎查看myis
  • mysql索引常见面试题

    mysql索引常见面试题 建表场景分析索引有查找和排序两大功能 建表 create table test1 id int not null PRIMARY KEY auto increment c1 char 10 c2 char 10 c
  • protobuf引入其它proto文件

    文章目录 同包内直接引用不同包中引用 同包内直接引用 文件结构 span class token operator span proto span class token operator span a proto span class t
  • JAVA_HOME配置

    Linux下配置 查询方法 xff1a span class token function which span java xff1a 查看Java执行命令位置 span class token function ls span lrt u
  • 经典进程同步问题(一)——生产者消费者问题

    目录 一 生产者消费者问题描述 二 解决思路 三 问题求解 xff1a 四 源码 五 运行结果 xff1a 一 生产者消费者问题描述 生产者消费者 xff08 producer customer xff09 问题是一个非常著名的进程同步问题