Linux多线程编程-生产者与消费者模型详解与实现(C语言)

1.什么是生成者与消费者模型

生产者-消费者模型是并发编程中的经典问题,描述了多个线程(或进程)如何安全、有效地共享有限的缓冲区资源。在这个模型中,有两种角色:

  1. 生产者(Producer):负责生成数据或者将数据放置到共享缓冲区中。

  2. 消费者(Consumer):负责从共享缓冲区中取出数据并进行处理或消费。

核心概念:

生产者和消费者模型主要涉及以下几个关键概念:

  • 共享缓冲区(Shared Buffer):生产者和消费者之间共享的有限大小的缓冲区。这个缓冲区可以是一个队列或者一个固定大小的数组。

  • 同步(Synchronization):确保生产者和消费者之间的正确协作,避免数据竞争和资源争用。例如,当缓冲区已满时,生产者应该等待;当缓冲区为空时,消费者应该等待。

  • 互斥(Mutual Exclusion):确保同一时刻只有一个线程(生产者或消费者)可以访问或操作共享缓冲区,以避免数据不一致或丢失。

具体案例:

假设有一个生产者和多个消费者的情形,以一个有界缓冲区(Bounded Buffer)为例:

  1. 共享缓冲区:假设有一个大小为10的数组作为共享缓冲区,用于存放生产者生产的产品。

  2. 生产者:负责生成产品,并将产品放入共享缓冲区中。如果缓冲区已满,生产者需要等待,直到有空间可以放置产品。

  3. 消费者:负责从共享缓冲区中取出产品并进行消费。如果缓冲区为空,消费者需要等待,直到有产品可以消费。

解决方案:

为了解决生产者-消费者模型中的同步和互斥问题,可以采用以下方法:

  • 互斥锁(Mutex):确保在同一时刻只有一个线程可以访问或修改共享缓冲区。例如,生产者和消费者在访问缓冲区前,首先要获取互斥锁,操作完成后释放锁。

  • 条件变量(Condition Variables):用于线程间的通信,如通知生产者缓冲区有空间可以放置新产品,或通知消费者缓冲区中有产品可以消费。

  • 信号量(Semaphores):用于控制对共享资源的访问,如控制缓冲区的空闲空间数量或产品数量。

示例代码:

使用C语言来实现,包括两个生产者线程、三个消费者线程,一个大小为10的共享缓冲区(使用链表实现),每个线程生成或消费一个数据后休眠1-2秒,并打印过程。我们将使用条件变量来实现线程的等待和唤醒机制。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>#define BUFFER_SIZE 10// 链表节点
typedef struct Node {int data;struct Node *next;
} Node;// 全局变量
Node *head = NULL;                  // 指向链表头部的指针
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  // 互斥锁
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;    // 缓冲区为空的条件变量
pthread_cond_t full = PTHREAD_COND_INITIALIZER;     // 缓冲区已满的条件变量
int count = 0;                      // 缓冲区当前数据项的数量// 生产者线程函数
void *producer_func(void *arg) {int id = *((int *)arg);  // 生产者线程的编号int data = 0;while (1) {// 生成数据项data++;// 获取互斥锁pthread_mutex_lock(&mutex);// 等待直到缓冲区有空间while (count == BUFFER_SIZE) {printf("Producer %d: Buffer is full. Waiting...\n", id);//条件变量 (empty):在缓冲区已满时,生产者线程会等待在 empty 条件变量上,直到有消费者取走数据并通知它。pthread_cond_wait(&empty, &mutex);}// 将数据项放入缓冲区,头插法,取节点的时候不用遍历链表Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = data;new_node->next = head;head = new_node;count++;printf("Producer %d: Produced data: %d\n", id, data);// 在生产者放入数据后,会通过 full 条件变量唤醒等待的消费者线程,告知它们可以消费数据了。pthread_cond_signal(&full);// 释放互斥锁pthread_mutex_unlock(&mutex);// 休眠1-2秒sleep(rand() % 2);}pthread_exit(NULL);
}// 消费者线程函数
void *consumer_func(void *arg) {int id = *((int *)arg);  // 消费者线程的编号while (1) {// 获取互斥锁pthread_mutex_lock(&mutex);// 等待直到缓冲区有数据while (head == NULL) {printf("Consumer %d: Buffer is empty. Waiting...\n", id);//条件变量 (full):在缓冲区为空时,消费者线程会等待在 full 条件变量上,直到有生产者放入数据并通知它。pthread_cond_wait(&full, &mutex);}// 从缓冲区取出数据项Node *temp = head;head = head->next;int data = temp->data;free(temp);count--;printf("Consumer %d: Consumed data: %d\n", id, data);// 在消费者取走数据后,会通过 empty 条件变量唤醒等待的生产者线程,告知它们可以继续生产数据。pthread_cond_signal(&empty);// 释放互斥锁pthread_mutex_unlock(&mutex);// 休眠1-3秒sleep(rand() % 3 + 1);}pthread_exit(NULL);
}int main() {pthread_t producers[2], consumers[3];int producer_ids[2] = {1, 2};  // 两个生产者线程的编号int consumer_ids[3] = {1, 2, 3};  // 三个消费者线程的编号// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&empty, NULL);pthread_cond_init(&full, NULL);// 创建生产者线程for (int i = 0; i < 2; ++i) {pthread_create(&producers[i], NULL, producer_func, (void *)&producer_ids[i]);}// 创建消费者线程for (int i = 0; i < 3; ++i) {pthread_create(&consumers[i], NULL, consumer_func, (void *)&consumer_ids[i]);}// 主线程等待所有子线程结束for (int i = 0; i < 2; ++i) {pthread_join(producers[i], NULL);}for (int i = 0; i < 3; ++i) {pthread_join(consumers[i], NULL);}// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&empty);pthread_cond_destroy(&full);return 0;
}

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

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

相关文章

<数据集>光伏板缺陷检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2400张 标注数量(xml文件个数)&#xff1a;2400 标注数量(txt文件个数)&#xff1a;2400 标注类别数&#xff1a;4 标注类别名称&#xff1a;[Crack,Grid,Spot] 序号类别名称图片数框数1Crack8688922Grid8248843S…

研究生发表期刊/会议必看,一文看懂A/B/C类和顶刊

主要看&#xff1a;中国计算机学会&#xff08;CCF&#xff09;推荐国际学术期刊 里面的划分等级 等级为&#xff1a;A类&#xff08;最难&#xff09;>B类&#xff08;中等难度&#xff09;>C类&#xff08;难度一般&#xff09; 本人这边计划&#xff1a;最低发C刊&a…

css-grid布局(栅格布局)

css新世界-auto-fit grid 一个比flex更强大的布局,适合做整体布局 grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); auto-fit的话有strech效果gap 不仅可以用于grid 也可用flex. 在grid-template-areas表示这个位置空着grid area 的 [a b]命名可重复命名 表示的…

大模型分布式训练并行技术

随着深度学习的发展&#xff0c;模型规模逐渐增大&#xff0c;数据量和计算需求也呈爆炸式增长。在单个计算设备上完成大模型的训练变得不切实际&#xff0c;因此&#xff0c;分布式训练成为了解决这一问题的关键。在分布式训练中&#xff0c;数据并行是一种非常有效的策略&…

【JavaScript脚本宇宙】JavaScript图表库大比拼:从实时数据到时间表,一网打尽

数据可视化利器&#xff1a;探索六款流行JavaScript图表库 前言 在Web开发中&#xff0c;数据可视化是一个非常重要的领域。随着JavaScript图表库的不断涌现&#xff0c;开发人员可以更轻松地创建各种交互式和实时的图表。本文将介绍几种流行的JavaScript图表库&#xff0c;包…

双向收发的信号应该在哪进行串联端接?分享几个实用设计方法!

高速先生成员--黄刚 经过上次高速先生的描述&#xff0c;相信大家已经掌握了串联端接的秘诀了&#xff0c;简单来说&#xff0c;那就是第一步&#xff1a;先看看芯片的驱动内阻&#xff0c;第二步&#xff1a;再用加起来50欧姆匹配的方法来选择适合的串阻值&#xff0c;第三步&…

【C语言报错已解决】格式化字符串漏洞(Format String Vulnerability)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引言&#xff1a;一、问题描述&#xff1a;1.1 报错示例&#xff1a;1.2 报错分析&#xff1a;1.3 解决思路&#xff…

HEROIC FANTASY WERE CREATURES PACK VOL 2 (幻想生物)

这个包收集了5种英雄幻想生物:狼人,狼人,狼人山羊,狼人鲨鱼和狼人蜘蛛。 狼人:27.5 Ktris Max//101个骨骼//4种材质//最多4096*4096个纹理//40个动画(11个是根运动变体) 狼人:15.83 Ktris//66个骨骼//3种材质//最多4096*4096个纹理//35个动画(9个是根运动变体) wereg…

Centos 使用nfs配置共享目录使docker集群所有容器日志统一主机访问

Centos 使用nfs配置共享目录&#xff0c;使docker集群所有容器日志统一存放在主机一个共享目录下&#xff0c;供开发人员访问查看 准备两台或以上Centos服务器 192.168.0.1 nfs服务器 192.168.0.2 nfs客户端 以root用户登录192.168.0.1服务器&#xff0c;执行以下操作 注意先…

excel根据数据批量创建并重命名工作表

需求 根据一列数据&#xff0c;批量创建并重命名工作表 做法 1. 右键该sheet&#xff0c;选择查看代码 2. 输入VBA代码 正向创建 Sub create_sheets_by_col()Dim num% 定义为integer*num Application.WorksheetFunction.CountA(Sheet1.Range("A:A")) num是非空…

木舟0基础学习Java的第十八天(IO流,字节流,字符流,缓冲)

IO流正常使用流程&#xff1a;1.抛异常 2.资源读写 3.关闭资源(从后往前关) 字节流&#xff1a;(拷贝推荐使用) 开发中一般不会抛出异常 用try{}catch(){} 也不推荐字节流读中文 FileInputStream:读 FileInputStream fsnew FileInputStream("e:/b.txt");//11111…

设计模式使用场景实现示例及优缺点(行为型模式——策略模式)

策略模式&#xff08;Strategy Pattern&#xff09; 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为设计模式&#xff0c;它能够在运行时选择最适合的算法或行为&#xff0c;同时能够将算法族封装成独立的类&#xff0c;并使它们之间可以相互替换。这种模式是通…

类形断言和和类型推导的区别是什么?

类型断言&#xff08;Type Assertion&#xff09;和类型推导&#xff08;Type Inference&#xff09;在TypeScript中的区别 如下&#xff1a; 定义&#xff1a; 类型断言&#xff1a;是程序员明确指定一个值的类型&#xff0c;即允许变量从一种类型更改为另一种类型。它不会进行…

接着探索Linux的世界 -- 基本指令(文件查看、时间相关、打包压缩等等)

话不多说&#xff0c;直接进入主题 一、cat指令 -- 查看目标文件的内容 语法&#xff1a;cat [选项][文件] 功能&#xff1a; 查看目标文件的内容 -b 对非空输出行编号 -n 对输出的所有行编号 -s 不输出多行空行 1、查看目标文件的内容 2、 -b 对非空输出行编号 3、-n 对…

24/07/11数据结构(6.1215)双链表实现-栈实现

像写单链表的一些功能接口一样,先来写一些双链表的接口熟悉一下它的主体框架: #include<stdio.h> #include<stdlib.h> typedef int LDataType; //双向带头循环链表的节点 typedef struct ListNode{ LDataType _data; //指向下一个节点的起始位置 str…

Kylin系列(九)与 Hadoop 集成:Kylin 如何在 Hadoop 生态中运作

目录 1. Kylin概述 2. Hadoop概述 3. Kylin与Hadoop集成的架构 4. 实现Kylin与Hadoop的集成 4.1 安装和配置Hadoop 4.2 安装和配置Hive 4.3 安装和配置Kylin 4.4 构建多维数据立方体 4.5 实现实时数据处理 5. Kylin与Hadoop集成的优势 6. 总结 在大数据时代&#xf…

项目部署笔记

1、安全组需开放&#xff08;如果不开放配置nginx也访问不到&#xff09; 2、域名解析配置IP(子域名也需配置IP&#xff0c;IP地址可以不同) 3、如果出现图片获其他的文件找不到的情况请仔细检查一下路径是否正确 4、服务器nginx配置SSL证书后启动报错&#xff1a; nginx: […

巧用 VScode 网页版 IDE 搭建个人笔记知识库!

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 巧用 VScode 网页版 IDE 搭建个人笔记知识库! 描述&#xff1a;最近自己在腾讯云轻量云服务器中部署了一个使用在线 VScode 搭建部署的个人Markdown在线笔记&#xff0c;考虑到在线 VScode 支持终…

Python 数据清洗与预处理

Python 数据清洗与预处理 在数据科学和机器学习的项目中&#xff0c;数据清洗与预处理是至关重要的一步。无论数据来源如何&#xff0c;原始数据通常都是不完整、不一致、含有噪声的&#xff0c;甚至可能包含错误。为了从这些原始数据中提取有价值的信息&#xff0c;并进行有效…

Day05-filebeat常用的输出组件,logstash的输入输出组件及date,grok,geoip过滤插件实战案例

Day05-filebeat常用的输出组件&#xff0c;logstash的输入输出组件及date&#xff0c;grok&#xff0c;geoip过滤插件实战案例 1、使用filebeat采集docker日志2、filebeat的input类型之filestream实战案例2.1 课堂练习案例2.2 将数据写入到本地文件案例2.3 写入数据到ES集群2.4…