C++ 生产者-消费者模式详细解析与代码实现

        在多线程编程中,生产者-消费者(Producer-Consumer)模式是一种经典的并发模型。它能够解决多线程环境下资源共享和任务调度的问题。本篇博客将从生产者-消费者模式的概念、实现方式、典型场景、以及具体的C++代码示例展开详细讲解。

一、生产者-消费者模式概述

生产者-消费者模式是一种通过缓冲区将生产者和消费者解耦的设计模式。生产者线程负责生成数据,而消费者线程负责消费数据。由于生产者和消费者的工作速度可能不同,因此缓冲区的存在使得它们可以独立运行。

1.1 主要问题

在没有缓冲区的情况下,如果生产者速度远快于消费者,生产者将不得不等待消费者处理完数据才能继续工作,反之亦然。而通过缓冲区,生产者可以将数据存入缓冲区后继续生产,而消费者则可以从缓冲区取数据进行消费。

1.2 常见场景

生产者-消费者模式在实际场景中非常常见,比如:

  • 日志处理系统:日志的生成速度可能远高于存储速度,生产者生成日志,消费者将日志写入文件。
  • 任务调度系统:生产者负责产生任务,消费者负责执行任务。
  • 数据流处理:生产者从网络或设备读取数据,消费者进行数据处理。

二、生产者-消费者模式的关键技术

要在多线程环境下实现生产者-消费者模式,需要解决以下几个技术问题:

  1. 线程安全的缓冲区:由于多个线程会同时操作缓冲区,因此需要确保缓冲区是线程安全的。
  2. 同步机制:生产者和消费者需要同步,确保缓冲区既不会过满,也不会为空。
  3. 线程阻塞与唤醒:当缓冲区满时,生产者应当阻塞等待,当缓冲区有数据时,消费者应该被唤醒开始工作。

2.1 使用 std::condition_variable 解决同步问题

在C++中,std::condition_variable 是一种条件变量,它可以用来实现线程之间的等待与通知机制。结合 std::mutex(互斥锁),它可以用于解决生产者-消费者之间的同步问题。

  • 等待 (wait):线程可以使用条件变量的 wait 方法,阻塞自己直到条件满足。
  • 通知 (notify_onenotify_all):当某个条件满足时,可以使用 notify_one 唤醒一个等待的线程,或使用 notify_all 唤醒所有等待线程。

2.2 使用 std::mutex 解决资源竞争问题

在多线程环境下,多个线程同时访问共享资源时,会产生数据竞争问题。为了解决这个问题,需要使用 std::mutex 来确保每次只有一个线程能够访问共享的缓冲区。

三、C++ 生产者-消费者模式实现

接下来我们将用C++编写一个完整的生产者-消费者模式示例。该示例中,生产者不断产生数据放入缓冲区,消费者从缓冲区中取出数据进行处理。我们使用 std::queue 来模拟缓冲区,并使用 std::mutexstd::condition_variable 来同步生产者与消费者的操作。

3.1 示例代码

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>// 共享缓冲区
std::queue<int> buffer;
// 最大缓冲区大小
const unsigned int BUFFER_SIZE = 10;// 互斥锁和条件变量
std::mutex mtx;
std::condition_variable cv_producer;
std::condition_variable cv_consumer;// 生产者线程函数
void producer(int id) {int product = 0;while (true) {std::unique_lock<std::mutex> lock(mtx);// 等待缓冲区有空位cv_producer.wait(lock, []() { return buffer.size() < BUFFER_SIZE; });// 生产数据并放入缓冲区product++;buffer.push(product);std::cout << "Producer " << id << " produced: " << product << std::endl;// 通知消费者有数据可取cv_consumer.notify_all();// 释放锁,生产间隔一段时间(模拟生产过程)lock.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}// 消费者线程函数
void consumer(int id) {while (true) {std::unique_lock<std::mutex> lock(mtx);// 等待缓冲区有数据cv_consumer.wait(lock, []() { return !buffer.empty(); });// 消费数据int consumed_product = buffer.front();buffer.pop();std::cout << "Consumer " << id << " consumed: " << consumed_product << std::endl;// 通知生产者缓冲区有空位cv_producer.notify_all();// 释放锁,消费间隔一段时间(模拟消费过程)lock.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(150));}
}int main() {// 创建生产者和消费者线程std::thread producer1(producer, 1);std::thread producer2(producer, 2);std::thread consumer1(consumer, 1);std::thread consumer2(consumer, 2);// 让主线程等待生产者和消费者线程(通常不会结束)producer1.join();producer2.join();consumer1.join();consumer2.join();return 0;
}

3.2 代码解析

  • 缓冲区:使用 std::queue<int> buffer 作为生产者与消费者共享的缓冲区。缓冲区的大小由 BUFFER_SIZE 限制。
  • 互斥锁和条件变量
    • std::mutex mtx 确保在同一时间只有一个线程能够访问缓冲区,避免数据竞争。
    • std::condition_variable cv_producercv_consumer 用来管理生产者和消费者的等待和唤醒操作。
  • 生产者函数:生产者函数 producer() 负责产生数据并放入缓冲区。如果缓冲区已满,生产者线程会等待消费者消费数据后继续生产。
  • 消费者函数:消费者函数 consumer() 负责从缓冲区中取出数据。如果缓冲区为空,消费者线程会等待生产者产生数据。

3.3 代码输出示例

运行上述代码后,生产者和消费者线程会交替工作,输出类似以下内容:

Producer 1 produced: 1
Producer 2 produced: 1
Consumer 1 consumed: 1
Producer 1 produced: 2
Consumer 2 consumed: 1
Producer 2 produced: 2
Consumer 1 consumed: 2

3.4 注意事项

  • 生产者和消费者的速度不同步时,通过条件变量和互斥锁来确保它们能够协调工作。
  • 缓冲区的大小可以根据应用场景进行调整。
  • 在实际开发中,可以进一步优化线程池的使用或加入更多控制机制。

四、生产者-消费者模式的优化与扩展

4.1 多生产者-多消费者

上面的例子中,我们实现了两个生产者和两个消费者的简单模型。在实际应用中,可以根据需要扩展为多个生产者和多个消费者,并使用线程池来管理它们。通过合适的同步和锁机制,能够保证整个系统的性能和安全性。

4.2 性能优化

在高并发场景下,线程的频繁切换和锁竞争可能会影响性能。可以采用一些优化策略:

  • 无锁队列:通过无锁算法可以降低锁竞争的开销。
  • 自旋锁:在某些情况下,使用自旋锁可以减少线程上下文切换的开销。

五、总结

生产者-消费者模式是一种常见且有效的并发编程模型。在C++中,通过 std::threadstd::mutexstd::condition_variable,我们可以轻松实现这一模式来协调多线程间的工作。本篇博客详细介绍了该模式的工作原理,并通过代码示例展示了它的实现。希望对你理解并应用生产者-消费者模式有所帮助。

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

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

相关文章

声音克隆工具CosyVoice

阿里的免费声音克隆工具CosyVoice CosyVoice 是阿里通义实验室在七月初开源的一款专注于自然语音合成的语音大模型&#xff0c;它具备多语言、多音色和细腻的情感控制能力。这个系统支持中文、英文、日文、粤语和韩语五种语言的语音生成&#xff0c;并且在语音合成的效果上远超…

WebShell流量特征检测_哥斯拉篇

90后用菜刀&#xff0c;95后用蚁剑&#xff0c;00后用冰蝎和哥斯拉&#xff0c;以phpshell连接为例&#xff0c;本文主要是对后三款经典的webshell管理工具进行流量分析和检测。 什么是一句话木马&#xff1f; 1、定义 顾名思义就是执行恶意指令的木马&#xff0c;通过技术手…

以编程方式运行仿真

可以通过两种方法以编程方式仿真模型&#xff1a; 通过 sim 命令。 通过运行按钮 使用 sim 命令执行仿真时&#xff0c;可以使用命令行对仿真执行操作。使用运行按钮执行仿真意味着您可以使用 Simulink UI 与仿真进行交互&#xff0c;以执行任何其他操作。下表详细说明了这两…

STM32G474之DAC

STM32G474分别使用CORDIC硬件和“math.h”的正弦值&#xff0c;从DAC1和DAC2输出。 1、DAC特点 PA4的附加功能为DAC1_OUT1&#xff0c;无需映射&#xff0c;直接将它配置为模拟功能&#xff0c;就可以使用了。 PA6的附加功能为DAC2_OUT1&#xff0c;无需映射&#xff0c;直接将…

使用PyTorch Lightning力量精简空间分析

大家好&#xff0c;随着人工智能热潮的全面兴起&#xff0c;PyTorch Lightning库正在获得越来越多的关注。其特别突出的地方在于简化复杂的机器学习操作&#xff0c;即使对于非开发者也是如此。深度学习和部分机器学习中的许多挑战性方面&#xff0c;如多GPU训练和实验跟踪&…

物料主数据的分类及编码规则概述

物料主数据分布在设计、工艺、生产、采购、销售、存储、物流过程中的多个业务部门中&#xff0c;类型繁杂且不易于管理&#xff0c;必须对于物料主数据进行分类管理。 本文将从物料主数据的分类原则、编码样例、编码规范以及编码设计等方面进行详细介绍&#xff0c;帮助企业更…

STM32高级定时器生成互补PWM的原理与代码实现

文章目录 前言一 CubeMx配置1.1 TIM1 Mode and Configuration1.2 Paramter Settings 二 程序代码三 仿真分析总结 前言 互补 PWM&#xff08;Complementary PWM&#xff09;是指一对逻辑状态互为反相的 PWM&#xff08;脉冲宽度调制&#xff09;信号。这种信号配置常见于电机控…

数字人实战第三天——wav2lip部署教程

wav2lip数字人 一、wav2lip简介 Wav2Lip 是一种通过将音频与视频中的嘴唇动作同步的技术&#xff0c;旨在生成与音频内容高度匹配的口型动画。其主要应用是让视频中的人物嘴唇动作与配音或其他音频输入精确同步&#xff0c;这在电影配音、虚拟主持人、在线教学、影视后期处理等…

秋招最新大模型算法面试,熬夜都要肝完它

&#x1f4a5;大家在面试大模型LLM这个板块的时候&#xff0c;不知道面试完会不会复盘、总结&#xff0c;做笔记的习惯&#xff0c;这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助&#xff0c;都附有完整答案&#xff0c;熬夜…

如何快速练习键盘盲打

盲打是指在不看键盘的情况下进行打字&#xff0c;这样可以显著提高打字速度和效率。以下是一些练习盲打的方法&#xff1a; 熟悉键盘布局&#xff1a;首先&#xff0c;你需要熟悉键盘上的字母和符号的位置。可以通过键盘图或者键盘贴纸来帮助记忆。 使用在线打字练习工具&…

CSS —— display属性

用于指定一个元素在页面中的显示方式 HTML中标签元素大体被分为三种类型&#xff1a;块元素、行内元素和行内块元素 块元素 &#xff1a;block 1.独占一行 2.水平方向&#xff0c;占满它父元素的可用空间&#xff08;宽度是父级的100%&#xff09; 3.垂直方向&#xff0c;占据的…

Idea 创建 Maven项目的时候卡死

文章目录 一、Archetype 和 Catalog1.1 Archetype&#xff08;原型&#xff09;1.2 Catalog&#xff08;目录&#xff09; 二、可能遇到的问题2.1 问题描述2.2 原因分析2.3 解决方案 参考资料 一、Archetype 和 Catalog 1.1 Archetype&#xff08;原型&#xff09; Archetype…

【自考zt】【软件工程】【21.10】

关键字&#xff1a; 软件需求基本性质、软件系统需求挑战、耦合&#xff08;高内容&#xff0c;低无直接&#xff09;、内聚&#xff08;初始化时间&#xff09;、uml包、rup边界类、测试首要目标、单元测试最后工作、性能需求 软件开发本质、软件需求规约三种风格、提炼、用…

比较差异 图片 视频

目录 两张图片像素差&#xff1a; 深度图和rgb图对齐 视频比较差异&#xff1a; 拼接2&#xff1a; 两张图片像素差&#xff1a; diffnp.clip(np.abs( img_mask.astype(np.int16))-img.astype(np.int16), 0, 255).astype(np.uint8) 深度图和rgb图对齐 # -*- coding: utf-…

9.7(QT.Day 1)

一、自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面 要求&#xff1a;每行代码都有注释 【需要用到的图片或者动图&#xff0c;自己去网上找】 1.mywidget.h代码 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include <QIcon> //图…

2024全国大学省数学建模竞赛A题-原创参考论文(部分+第一问代码)

一问题重述 1.1 问题背景 "板凳龙"&#xff0c;又称"盘龙"&#xff0c;是浙闽地区的传统地方民俗文化活动。这种独特的表演艺术形式融合了中国传统龙舞的精髓和地方特色&#xff0c;展现了人们对美好生活的向往和对传统文化的传承。 在板凳龙表演中&am…

为elementui的el-date-picker时间选择器添加快捷选项

1、效果图 2、实现方法 直接在elementui的时间选择器上修改&#xff0c;添加shorcuts选项&#xff0c;但是样式要自己修改。 有几个注意点&#xff1a; 1&#xff09;如图我是选中后有显示背景颜色的&#xff0c;也就意味着要给选中的选项添加类名&#xff0c;elementui没有…

一. 从Hive开始

1. 怎么理解Hive Hive不能理解成一个传统意义上的数据库&#xff0c;应该理解成一个解决方案。 是Hadoop在hdfs和mapreduce之后才出现的一个结构化数据处理的解决方案。 Hdfs解决了大数据的存储问题&#xff0c;mapreduce解决了数据的计算问题。 一切似乎很美好。 但是使用成本…

微信小程序页面制作——个人信息

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

探索fastFM:Python中的高效推荐系统库

文章目录 &#x1f680; 探索fastFM&#xff1a;Python中的高效推荐系统库背景&#xff1a;为何选择fastFM&#xff1f;快照&#xff1a;fastFM是什么&#xff1f;安装指南&#xff1a;如何将fastFM加入你的项目&#xff1f;快速入门&#xff1a;五个基础函数的使用实战演练&am…