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.rhkb.cn/news/377124.html

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

相关文章

Docker 安装ros 使用rviz 等等图形化程序

Docker 安装ros 使用rviz 等等图形化程序 ubuntu 版本与ros 发行版本对应 如何安装其它版本ros 此时考虑使用docker 易于维护 地址&#xff1a; https://hub.docker.com/r/osrf/ros 我主机是 ubuntu22.04 使用这个标签 melodic-desktop-full 1 clone 镜像到本机 docker pu…

OpenCV:python图像旋转,cv2.getRotationMatrix2D 和 cv2.warpAffine 函数

前言 仅供个人学习用&#xff0c;如果对各位朋友有参考价值&#xff0c;给个赞或者收藏吧 ^_^ 一. cv2.getRotationMatrix2D(center, angle, scale) 1.1 参数说明 parameters center&#xff1a;旋转中心坐标&#xff0c;是一个元组参数(col, row) angle&#xff1a;旋转角度…

html(抽奖设计)

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>抽奖</title><style type"text/css">* {margin: 0;padding: 0;}.container {width: 800px;height: 800px;border: 1px dashed red;position: absolut…

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

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

近期几首小诗汇总-生活~卷

生活 为生活飘零&#xff0c;风雨都不阻 路见盲人艰&#xff0c;为她心点灯 贺中科大家长论坛成立十五周年 科学家园有喜贺 园外丑汉翘望中 曾一学子入我科 正育科二盼长大 憧憬也能入此家 与科学家论短长 园外翘首听高论 发现有隙入此坛 竟然也能注册成 入园浏览惶然立 此贴…

零信任的架构结合模块化沙箱,实现一机两用的解决方案

零信任沙箱是深信达提出的一种数据安全解决方案&#xff0c;它将零信任原则与SDC沙箱技术的优势相结合。零信任原则是一种安全概念&#xff0c;核心思想是“永不信任&#xff0c;总是验证”。它要求对每一个访问请求都进行严格的身份验证和授权&#xff0c;无论请求来源于内部还…

Qt Quick qml自定义控件:qml实现电池控件

qml入门进阶专栏地址:https://blog.csdn.net/yao_hou/category_9951228.html?spm=1001.2014.3001.5482 本篇博客介绍如何使用qml来实现电池控件,效果图如下: 下面给出实现代码 Battery.qml /*电池组件*/import QtQuick 2.15 import QtQuick.Controls 2.15Rectangle {id: b…

ES13的4个改革性新特性

1、类字段声明 在 ES13 之前,类字段只能在构造函数中声明, ES13 消除了这个限制 // 之前 class Car {constructor() {this.color = blue;this.age = 2

EXSI 实用指南 2024 -编译环境 Ubuntu 安装篇(二)

1. 引言 在当今的虚拟化领域&#xff0c;VMware ESXi 是备受推崇的虚拟化平台&#xff0c;广泛应用于企业和个人用户中。它以卓越的性能、稳定的运行环境和丰富的功能&#xff0c;为用户提供了高效的硬件资源管理和简化的 IT 基础设施维护。然而&#xff0c;如何在不同操作系统…

STM32第十九课:FreeRTOS移植和使用

目录 需求一、FreeRtos概要二、移植FreeRtos1.复制源码2.内存空间分配和内核相关接口3.FreeRTOSConfig.h4.在工程中添加.c.h 三、任务块操作1.创建任务2.任务挂起&#xff0c;恢复&#xff0c;删除 四、需求实现代码 需求 1.将FreeRtos&#xff08;嵌入式实时操作系统&#xf…

ts使用typeorm实现db创建

1.新建基础架构 ①创建项目文件名, mkdir ‘名称’ ->cd ‘文件名’ -> mkdir ‘src’->npm init mkdir fileName cd fileName mkdir src npm init在当前项目名目录下执行npm init,按照默认执行就会创建package.json. 之后执行 npm i jest/globals casl/ability bcr…

755M全球山脉数据集分享

我们在《548M高精度全球国界数据》和《270M全球流域矢量数据》文中&#xff0c;为你分享过全球的国界数据和水系流域数据。 现在再为你分享755M全球山脉数据集&#xff0c;请在文末查看该数据的领取方法。 755M全球山脉数据集 全球山脉数据集&#xff0c;提供了在全球山地生…

【企业级监控】Zabbix监控网站并发连接数

Zabbix自定义监控项与触发器 文章目录 Zabbix自定义监控项与触发器资源列表基础环境前言一、什么是zabbix的Key值二、获取远程Key值2.1、获得主机的Key值2.2、被监控端安装Agent2.3、zabbix_get命令获取Agent数据举例2.3.1、zabbx_get获取cpu核心数2.3.2、获取目标主机系统和内…

ESP32CAM物联网教学11

ESP32CAM物联网教学11 霍霍webserver 在第八课的时候&#xff0c;小智把乐鑫公司提供的官方示例程序CameraWebServer改成了明码&#xff0c;这样说明这个官方程序也是可以更改的嘛。这个官方程序有四个文件&#xff0c;一共3500行代码&#xff0c;看着都头晕&#xff0c;小智决…

opencascade AIS_InteractiveContext源码学习8 trihedron display attributes

AIS_InteractiveContext 前言 交互上下文&#xff08;Interactive Context&#xff09;允许您在一个或多个视图器中管理交互对象的图形行为和选择。类方法使这一操作非常透明。需要记住的是&#xff0c;对于已经被交互上下文识别的交互对象&#xff0c;必须使用上下文方法进行…

探索IP形象设计:快速掌握设计要点

随着市场竞争的加剧&#xff0c;越来越多的企业开始关注品牌形象的塑造和推广。在品牌形象中&#xff0c;知识产权形象设计是非常重要的方面。在智能和互联网的趋势下&#xff0c;未来的知识产权形象设计可能会更加关注数字和社交网络。通过数字技术和社交媒体平台&#xff0c;…

年轻人「躺平」、「摆烂」现象的根源是什么?

年轻人「躺平」、「摆烂」现象的根源是什么? 穷人没有资格躺平 我可以躺平吗?当然可以了! 对于有些人来说是躺平在房车里,直接开到命运的终点;而你是躺在马路中间,被命运的车轮反复碾压。 中国一线城市的00后,他们的父母多是没有哥哥、姐姐、弟弟、妹妹的独生子女,…

S7-200smart与C#通信

https://www.cnblogs.com/heizao/p/15797382.html C#与PLC通信开发之西门子s7-200 smart_c# s7-200smart通讯库-CSDN博客https://blog.csdn.net/weixin_44455060/article/details/109713121 C#上位机读写西门子S7-200SMART PLC变量 教程_哔哩哔哩_bilibilihttps://www.bilibili…

昇思25天学习打卡营第19天|sea_fish

打卡第19天。本次学习的内容为生成式中的Diffusion扩散模型。记录学习的过程。 模型简介 什么是Diffusion Model&#xff1f; 如果将Diffusion与其他生成模型&#xff08;如Normalizing Flows、GAN或VAE&#xff09;进行比较&#xff0c;它并没有那么复杂&#xff0c;它们都…

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

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