首页 > 编程语言 >生产者消费者c++ 讲解和代码示例

生产者消费者c++ 讲解和代码示例

时间:2024-10-12 21:47:08浏览次数:11  
标签:std 示例 int lock c++ 线程 讲解 缓冲区 cv

生产者-消费者问题的C++讲解和代码示例

一、问题描述

生产者-消费者问题是经典的多线程同步问题,涉及两个类型的线程:

  • 生产者线程:负责生成数据并放入共享缓冲区。
  • 消费者线程:负责从共享缓冲区取出数据进行处理。

关键挑战在于:

  • 同步:确保生产者和消费者在访问共享缓冲区时不发生冲突。
  • 互斥:防止多个线程同时修改缓冲区导致数据不一致。
  • 避免死锁:设计合理的等待和通知机制,防止线程无限期地等待。

二、解决方案

在C++中,可以使用以下同步机制:

  • std::mutex:互斥锁,用于保护共享数据的访问。
  • std::condition_variable:条件变量,用于线程间的等待和通知。
  • std::unique_lock<std::mutex>:配合条件变量使用的锁。

三、代码示例

下面是一个使用C++11线程库实现的生产者-消费者模型。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>

std::mutex mtx; // 互斥锁
std::condition_variable cv; // 条件变量
std::queue<int> buffer; // 共享缓冲区
const unsigned int MAX_BUFFER_SIZE = 10; // 缓冲区最大容量

void producer(int id) {
    int data = 0;
    while (true) {
        // 模拟生产数据的时间
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

        std::unique_lock<std::mutex> lock(mtx);
        // 等待缓冲区未满
        cv.wait(lock, []() { return buffer.size() < MAX_BUFFER_SIZE; });

        // 生产数据并放入缓冲区
        buffer.push(data);
        std::cout << "生产者 " << id << " 生产了数据 " << data << std::endl;
        data++;

        // 通知消费者
        cv.notify_all();
    }
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        // 等待缓冲区不为空
        cv.wait(lock, []() { return !buffer.empty(); });

        // 从缓冲区取出数据
        int data = buffer.front();
        buffer.pop();
        std::cout << "消费者 " << id << " 消费了数据 " << data << std::endl;

        // 通知生产者
        cv.notify_all();

        // 模拟处理数据的时间
        lock.unlock(); // 解锁以允许生产者继续生产
        std::this_thread::sleep_for(std::chrono::milliseconds(150));
    }
}

int main() {
    std::thread producers[2], consumers[2];

    // 启动生产者线程
    for (int i = 0; i < 2; ++i) {
        producers[i] = std::thread(producer, i);
    }

    // 启动消费者线程
    for (int i = 0; i < 2; ++i) {
        consumers[i] = std::thread(consumer, i);
    }

    // 等待线程完成(此示例中线程是无限循环,可根据需要修改)
    for (int i = 0; i < 2; ++i) {
        producers[i].join();
        consumers[i].join();
    }

    return 0;
}

四、代码解析

1. 全局变量

  • 互斥锁 mtx:保护对共享缓冲区的访问,防止数据竞争。
  • 条件变量 cv:用于线程间的等待和通知机制。
  • 共享缓冲区 buffer:存放生产者生成的数据,供消费者消费。
  • MAX_BUFFER_SIZE:限制缓冲区的最大容量,防止过度填充。

2. 生产者函数

void producer(int id) {
    int data = 0;
    while (true) {
        // 模拟生产时间
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

        std::unique_lock<std::mutex> lock(mtx);

        // 等待缓冲区未满
        cv.wait(lock, []() { return buffer.size() < MAX_BUFFER_SIZE; });

        // 放入数据
        buffer.push(data);
        std::cout << "生产者 " << id << " 生产了数据 " << data << std::endl;
        data++;

        // 通知可能等待的消费者
        cv.notify_all();
    }
}
  • 使用unique_lock获取互斥锁,确保对缓冲区的独占访问。
  • 使用cv.wait等待缓冲区有空间(未满)。
  • 生产数据并放入缓冲区。
  • 使用cv.notify_all通知等待的消费者线程。

3. 消费者函数

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);

        // 等待缓冲区不为空
        cv.wait(lock, []() { return !buffer.empty(); });

        // 取出数据
        int data = buffer.front();
        buffer.pop();
        std::cout << "消费者 " << id << " 消费了数据 " << data << std::endl;

        // 通知可能等待的生产者
        cv.notify_all();

        // 模拟处理数据的时间
        lock.unlock(); // 释放锁
        std::this_thread::sleep_for(std::chrono::milliseconds(150));
    }
}
  • 获取互斥锁,确保对缓冲区的独占访问。
  • 使用cv.wait等待缓冲区有数据(不为空)。
  • 消费数据并从缓冲区移除。
  • 使用cv.notify_all通知等待的生产者线程。
  • 在处理数据时释放锁,允许其他线程访问缓冲区。

4. 主函数

int main() {
    std::thread producers[2], consumers[2];

    // 启动生产者和消费者线程
    for (int i = 0; i < 2; ++i) {
        producers[i] = std::thread(producer, i);
        consumers[i] = std::thread(consumer, i);
    }

    // 等待线程完成(无限循环,实际应用中可添加终止条件)
    for (int i = 0; i < 2; ++i) {
        producers[i].join();
        consumers[i].join();
    }

    return 0;
}
  • 创建两个生产者线程和两个消费者线程。
  • 使用join等待线程完成(此示例中线程是无限循环)。

五、注意事项

  • 互斥锁的正确使用:确保在访问共享资源时始终持有互斥锁。
  • 条件变量的搭配使用cv.wait需要配合unique_lock和条件函数。
  • 避免虚假唤醒:条件函数应始终在循环中检查,cv.wait会自动处理这种情况。
  • 性能优化:根据实际需求调整缓冲区大小和线程数量。

六、总结

生产者-消费者问题是并发编程中的重要模型,通过C++的线程和同步机制,可以有效地实现线程间的协作。关键在于正确地使用互斥锁和条件变量,确保数据安全和线程同步。

标签:std,示例,int,lock,c++,线程,讲解,缓冲区,cv
From: https://blog.csdn.net/qq_43552933/article/details/142884015

相关文章

  • 107-免杀对抗-C&C++&溯源ShellCode上线&混淆变异算法&回调编译执行
    知识点#知识点:1、ShellCode-分析&朔源&感知2、ShellCode-混淆&编码&算法3、回调执行解析-API&汇编&句柄#章节点:编译代码面-ShellCode-混淆编译代码面-编辑执行器-编写编译代码面-分离加载器-编写程序文件面-特征码定位-修改程序文件面-加壳花指令-资源代码加载面-Dll......
  • C, C++中的宏是什么
    在C++中,宏(Macro)是一种由预处理器(Preprocessor)处理的文本替换机制。宏通过#define指令定义,可以在编译前对代码进行替换,从而实现代码的复用、条件编译等功能。宏在C和C++中都广泛使用,但由于其特性,也可能带来一些潜在的问题,因此在使用时需要谨慎。宏的基本概念宏是在预处......
  • 用C/C++构建自己的Redis——第六章、事件循环和计时器
    用C/C++构建自己的Redis——第六章、事件循环和计时器文章目录用C/C++构建自己的Redis——第六章、事件循环和计时器前言一、超时和计时器二、链表三、事件循环四、链表排序4.1寻找最近的计时器4.2激活计时器4.3维护计时器五、测试总结前言这一章我们将一起学......
  • C++基础——书写“Hello World“
    C++基础——书写"HelloWorld"一、前言二、书写"HelloWorld"1.头文件2.主文件3.整体代码4.运行结果三、总结一、前言首先为大家介绍一下什么是C++。上述描述来自于百度百科!!!二、书写"HelloWorld"1.头文件#include"stdafx.h"#include<iostream>usingnam......
  • 基于数据可视化+微信小程序+SpringBoot的餐桌点餐小程序平台设计和实现(源码+论文+部
    博主介绍:CSDN毕设辅导第一人、全网粉丝50W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HLMT、J......
  • c++(自创游戏7.1)
    上代码!#include<bits/stdc++.h>#include<windows.h>usingnamespacestd;intmain(){inta,b,c,d,e,e1,e2,e3,e4,n; n=0; if(n==0)cout<<"这天,你跟往常一样,准备去上班。";n++; Sleep(2000); cout<<endl; if(n==1)cout<<"走到办公......
  • 基于Spring Boot的班级综合测评管理系统的设计与实现,LW+源码+讲解
    1绪论1.1课题研究的背景随着电子技术的普及和快速发展,线上管理系统被广泛的使用,有很多事业单位和商业机构都在实现电子信息化管理,班级综合测评管理也不例外,由比较传统的人工管理转向了电子化、信息化、系统化的管理。传统的班级综合测评管理系统,一开始都是手工记录,然后将手......
  • 基于SpringBoot的大学生体质测试管理系统设计与实现,LW+源码+讲解
    摘要大学生体质测试管理系统提供给用户一个简单方便体质测试管理信息,通过留言区互动更方便。本系统采用了B/S体系的结构,使用了java技术以及MYSQL作为后台数据库进行开发。系统主要分为系统管理员、教师和用户三个部分,系统管理员主要功能包括首页、个人中心、用户管理、教师管......
  • 基于SpringBoot的家乡特色推荐系统设计与实现,LW+源码+讲解
    摘要在Internet高速发展的今天,我们生活的各个领域都涉及到计算机的应用,其中包括家乡特色推荐的网络应用,在外国家乡特色推荐系统已经是很普遍的方式,不过国内的管理网站可能还处于起步阶段。家乡特色推荐系统采用java技术,基于springboot框架,mysql数据库进行开发,实现了首页,个人......
  • 链表排序算法(C++):数组辅助排序、插入排序、归并排序
    文章目录借助数组排序插入排序归并排序测试用例数组排序算法参考:冒泡排序、插入排序、选择排序、归并排序、快速排序算法(C++实现)-CSDN博客链表排序算法参考:链表排序总结(全)(C++)-CSDN博客这里主要介绍三种链表排序方法,第一种是借助数组进行排序的方法,第二种是插入排......