Linux 实验:记录型信号量 生产者-消费者问题详解

2023-05-16

进程同步问题是一个非常重要且相当有趣的问题,因而吸引了很多学者对他进行研究。本文就选取其中较为代表性的生产者-消费者问题来进行学习,以帮助我们更好的理解进程同步的概念及实现方法。

一、问题描述

有一群生产者进程在生产产品,并将这些产品提供给消费者进程进行消费。为了使生产这边进程与消费进程能够并发的执行,在两者之间设置了一个具有n 个缓冲区的缓冲池,生产者进程将其他生产的产品放到一个缓冲区中,消费者进程可以从一个缓冲区中取走产品去消费。

需要注意的是,尽管所有的生产者和消费者都是以异步的方式运行的,但是他们之间必须保持同步,

既不允许消费者进程在缓冲区为空时去取产品,也不允许生产者进程在缓冲区已满且产品尚未被取走时向缓冲区投放产品。

https://img-blog.csdnimg.cn/20200720151851117.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NpZWxsZWU=,size_16,color_FFFFFF,t_70

下图是一个生产者与消费者进程执行的流程图,比图中我们可以很清晰的看到上述的三个进程间的关系,其中生产者和消费者中操作缓冲区都需要先进行申请,也就是进入区,操作完成后要执行释放,也就是退出区,通过这样来实现对缓冲池的互斥访问。

二、问题分析

  • 缓冲池一次只能有一个进程访问。
  • 只要缓冲池未满,生产者就可以把产品放入缓冲区。
  • 只要缓冲池未空,消费者就要可以从缓冲区中取走产品。

通过图中的贯通两个进程的虚线来实现生产者和消费者的同步关系。

https://img-blog.csdnimg.cn/20200720152337287.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NpZWxsZWU=,size_16,color_FFFFFF,t_70

三、信号量设置

由于有界缓冲池是一个临界资源,必须互斥使用,这时可以利用互斥信号量 mutex 来实现诸进程对缓冲池的互斥使用。因为是互斥信号量,所以 mutex 初值为 1。

另外,可以设置两个同步信号量:一个表示缓冲池中空缓冲区的数目,用empty 表示,初值 为缓冲池的大小 n;另一个表示已满缓冲区的数目,即缓冲池中消息的数量,用 full 表示,初值为 0;

除了信号量外,我们可以使用循环链表来表示有界缓冲池,假设缓冲池的大小为 n,我们用 buffer[n] 来表示,另外还有两个队首指针in 和 队尾指针 out ,其初值都为 0。

四、记录型信号量解决生产者-消费者问题

首先我们使用记录型信号量来解决生产者-消费者问题,根据上面的分析,我们先给出伪代码:

int in=0, out=0;

// item 为消息的结构体
item buffer[n];
semaphore mutex=1, empty=n, full=0;// 初始化信号量
void producer(){
	do{
// 生产者产生一条消息
		producer an item next p;
		......
// 判断缓冲池中是否仍有空闲的缓冲区P(empty);
// 判断是否可以进入临界区(操作缓冲池)P(mutex);
// 向缓冲池中投放消息
		buffer[in] = nextp;
// 移动入队指针
		in = (in+1) % n;
//退出临界区,允许别的进程操作缓冲池V(mutex);
// 缓冲池中数量的增加,可以唤醒等待的消费者进程。V(full);
	}while(true);
}

void consumer(){
	do{
//判断缓冲池中是否有非空的缓冲区(消息)P(full);
// 判断是否可以进入临界区(操作缓冲池)P(mutex);
		nextc = buffer[out];
// 移动出队指针
		out = (out+1) % n;
// 退出临界区,允许别的进程操作缓冲池V(mutex);
// 缓冲池中空缓冲区数量加1, 可以唤醒等待的生产者进程V(empty);
// 消费消息
		consumer the item in next c;
	}while(true);
}

通过上面的伪代码,我们可以看到,在每个程序中用于实现互斥的 P(mutex) 和 V(mutex) 必须成对的出现,并且要出现在同一个程序中;对于用于控制进程同步的信号量 full 和 empty,其P/V 操作也必须成对的出现,但他们分别处于不同的程序之中。

还有比较重要的就是,每个程序中多个P操作顺序不能颠倒,比如,应先执行对资源信号量的P操作-P(empty), 再执行对互斥信号量的P操作- P(mutex), 否则可能会因为持有了互斥锁,但是没有空闲的缓冲区而导致生产者进程阻塞,但是别的进程又无法进入临界区,导致发生死锁。

上代码!!!


```c
#include <stdio.h>   
#include <unistd.h>//  sleep的头文件
#include <pthread.h>    //pthread的头文件
#include <semaphore.h>
#include <stdlib.h>  //包含exit的头文件
#define N 100
#define true 1
#define producerNum  3   //生产者数目
#define consumerNum  3   //消费者数目
//线程方法pthread  gcc -o main.c main -pthread  需要pthread头文件,但pthread不是linux的头文件需要链接
typedef int semaphore;    //类型定义 int型 信号量
typedef int item;        
// item 为消息的结构体
item buffer[N] = {0};
int in = 0;//队首指针in 和 队尾指针 out ,其初值都为 0
int out = 0;
int nextp = 0;//产品数目
// 初始化信号量
semaphore mutex = 1, empty = N, full = 0;
/*用互斥信号量 mutex 来实现诸进程对缓冲池的互斥使用。因为是互斥信号量,所以 mutex 初值为 1
由于有界缓冲池是一个临界资源,必须互斥使用,这时可以利用互斥信号量 mutex 来实现诸进程对缓冲池的互斥使用。
因为是互斥信号量,所以 mutex 初值为 1。
另外,可以设置两个同步信号量:一个表示缓冲池中空缓冲区的数目,用empty 表示,初值 为缓冲池的大小 n;
另一个表示已满缓冲区的数目,即缓冲池中消息的数量,用 full 表示,初值为 0;
除了信号量外,我们可以使用循环链表来表示有界缓冲池,假设缓冲池的大小为 n,
我们用 buffer[n] 来表示,另外还有两个队首指针in 和 队尾指针 out ,其初值都为 0。*/
void * producer(void * a){
    do{
        // 生产者产生一条消息
        nextp++;
        // 判断缓冲池中是否仍有空闲的缓冲区P(empty);
        while(empty <= 0){
            printf("缓冲区已满!\n");
        }  //如果满了 ,阻塞
        sleep(1);// 等待唤醒
        // 判断是否可以进入临界区(操作缓冲池)P(mutex);
        while(mutex <= 0);
        empty--;
        mutex--;              
        printf("生产一个产品ID%d, 缓冲区位置为%d\n",nextp,in);
        // 向缓冲池中投放消息  
        buffer[in] = nextp;
        // 移动入队指针
        in = (in + 1) % N;
        //退出临界区,允许别的进程操作缓冲池V(mutex);
        mutex++;
        // 缓冲池中数量的增加,可以唤醒等待的消费者进程。V(full);
        full++; 
        
    } while(true);
}

void * consumer(void *b){
    do{
        //判断缓冲池中是否有非空的缓冲区(消息)P(full);  
        while(full <= 0){
            printf("缓冲区为空!\n"); //如果空,阻塞
        }     
        sleep(1);//等待唤醒       
        // 判断是否可以进入临界区(操作缓冲池)P(mutex);   如果缓冲区是没有产品就没必要进入临界区
        while(mutex <= 0);
        mutex--;      
        full--; 
        // 从池中取出信息
        int nextc = buffer[out];
        // 移动出队指针
        out = (out + 1) % N;
        // 退出临界区,允许别的进程操作缓冲池V(mutex);
        // 缓冲池中空缓冲区数量加1, 可以唤醒等待的生产者进程V(empty);
        mutex++;
        empty++;         
       
        printf("\t\t\t\t消费一个产品ID%d,缓冲区位置为%d\n", nextc,out);
        // 消费消息
    }while(true);
}
//主函数部分的pthread没有搞明白。
int main()
{

    pthread_t threadPool[producerNum+consumerNum];
    int i;
    for(i = 0; i < producerNum; i++)
    {
        pthread_t temp;
        if(pthread_create(&temp, NULL, producer, NULL) == -1)
        {
            printf("ERROR, fail to create producer%d\n", i);
            exit(1);
        }
        threadPool[i] = temp;
    }//创建生产者进程放入线程池


    for(i = 0; i < consumerNum; i++)
    {
        pthread_t temp;
        if(pthread_create(&temp, NULL, consumer, NULL) == -1)
        {
            printf("ERROR, fail to create consumer%d\n", i);
            exit(1);
        }
        threadPool[i+producerNum] = temp;
    }//创建消费者进程放入线程池


    void * result;
    for(i = 0; i < producerNum+consumerNum; i++){
        if(pthread_join(threadPool[i], &result) == -1){
            printf("fail to recollect\n");
            exit(1);
        }
    }//运行线程池
    return 0;
}
可以说是最偷懒的行为了,直接++--比较粗暴!!!
部分代码参考: @Reacubeth

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

Linux 实验:记录型信号量 生产者-消费者问题详解 的相关文章

随机推荐

  • Ubuntu部分图标缺失,包括部分系统图标

    ubuntu部分图标缺失 这里说的缺失不是指图标不会显示 xff0c 而是说图标虽然会显示 xff0c 但是显示不正确 比如显示为一个空白方块或者红色的 34 禁止 34 图标 简要列出部分缺失的图标 xff1a 文件夹图标wifi图标 x
  • python 添加图例_Python | 在图上添加图例

    python 添加图例 Adding legend is the best way to label data series plotted on a graph Matplotlib has an inbuilt defined func
  • java有哪些集合类型?集合类的特点

    Java属于入门容易 xff0c 天花板却极高的编程语言 java有哪些集合类型 对于java工程师来说技术的不断发展 需要不断学习java进阶知识 为了帮助大家巩固基础 xff0c 本文解答了java有哪些集合类型 集合类的特点是什么 x
  • MATLAB(一)基本操作与矩阵输入

    文章目录 前言一 Matlab视窗二 基本操作与矩阵输入1 把MATLAB当做计算机2 初等数学函数Exercise练习 2 嵌入函数3 特殊变量和常量4 MATLAB调用优先5 数字显示格式长Exercise练习 6 命令行终端7 部分函
  • MATLAB(六)图形界面_GUI_程式设计

    文章目录 前言MATLAB GUI Programs启动GUI程序对齐组件给按钮标上标签GUI脚本结构function untitled OpeningFcn对象的回调Set the axes for PlottingExercise练习P
  • Excel 精选28个技巧

    文章目录 前言1 一键求和2 一键插入柱形图3 单元格内强制换行4 快速移动资料5 快速生成下拉式功能表6 计算带单位的数据7 小写金额转大写8 快速输入 9 批量添加下划线10 文字随单元格大小变化11 图片随单元格大小变化12 快速提取
  • 调度算法——时间片轮转、优先级、多级反馈队列(例题详细!!!)

    文章目录 前言知识总览时间片轮转 xff08 RR Round Robin xff09 优先级调度算法多级反馈队列调度算法知识回顾与重要考点 前言 此篇文章是我在B站学习时所做的笔记 xff0c 大部分图片都是课件老师的PPT xff0c
  • 生产者-消费者问题(有例题!!!)

    文章目录 前言问题描述如何实现思考 xff1a 能否改变相邻P V操作的顺序 知识回顾与重要考点 前言 此篇文章是我在B站学习时所做的笔记 xff0c 大部分图片都是课件老师的PPT xff0c 方便复习用 此篇文章仅供学习参考 提示 xf
  • 计算机网络习题——循环冗余校验

    3 07 要发送的数据为1101011011 采用CRC的生成多项式是 P X 61 X 4 43 X 43 1 试求应添加在数据后面的余数 xff08 1 xff09 若要发送的数据在传输过程中最后一个1变成了0 xff0c 即变成了11
  • 计算机网络课后答案(谢希仁第八版)

    计算机网络课后答案 谢希仁第八版
  • linux系统 删除文件命令

    Linux系统下删除文件是一个非常高频的需求 xff0c 几乎每天都会遇到 xff0c 所以rm命令是一个非常常用Linux命令 rm命令是英文单词 remove 的缩写 xff0c 它主要作用是 xff1a 1 删除文件 xff1b 2
  • 常见的HTTP状态码列表

    HTTP状态码列表 状态码 状态码英文名称 中文描述 1xx xff08 信息性状态码 xff09 xff1a 请求已被接受 xff0c 需要继续处理 100 Continue 继续 客户端应继续其请求 101 Switching Prot
  • 二进制的加减法_二进制加减法

    二进制的加减法 1 二进制加法 1 Binary Addition Since binary numbers consist of only two digits 0 and 1 so their addition is different
  • SQL注入攻击方法

    SQL注入攻击是一种利用Web应用程序中存在的安全漏洞 xff0c 通过在输入框中插入恶意的SQL代码 xff0c 从而实现对数据库的非法操作 以下是一些常见的SQL注入攻击方法 xff1a 使用单引号 xff08 39 xff09 进行字
  • 利用Python+selenium技术,实现浏览器基本操作详解,代码有详细注释

    首先 xff0c 需要安装selenium库和对应的浏览器驱动程序 以Chrome浏览器为例 xff0c 可以使用以下命令安装selenium和chromedriver xff1a pip install selenium 然后 xff0c
  • &和&&的区别(简单易懂)

    amp xff08 按位与 xff09 和 amp amp xff08 逻辑与 xff09 的区别如下 xff1a 1 amp amp 具有短路功能 xff0c 而 amp 不具有短路功能 2 当 amp 运算符两侧的表达式的结果均为真时
  • Spring框架学习笔记

    一 什么是Spring框架 Spring框架是由于软件开发的复杂性而创建的 Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情 然而 xff0c Spring的用途不仅仅限于服务器端的开发 从简单性 可测试性和松耦
  • 人工智能——DBSCAN密度聚类(Python)

    目录 1 概述 1 1 概念 1 2 DBSCAN数据点分类 2 DBSCAN算法流程 2 1 DBSCAN算法流程 xff1a 2 2 举例 3 案例1 xff08 Python实现 xff09 3 1 案例 3 2 Python实现 3
  • @RequestMapping的参数和用法

    在Spring MVC 中使用 64 RequestMapping 来映射请求 xff0c 也就是通过它来指定控制器可以处理哪些URL请求 xff0c 相当于Servlet中在web xml中配置 源码 xff1a 该注解说明可以在类和方法
  • Linux 实验:记录型信号量 生产者-消费者问题详解

    进程同步问题是一个非常重要且相当有趣的问题 xff0c 因而吸引了很多学者对他进行研究 本文就选取其中较为代表性的生产者 消费者问题来进行学习 xff0c 以帮助我们更好的理解进程同步的概念及实现方法 一 问题描述 有一群生产者进程在生产产