C++ Thread多线程并发记录(8)生产者-消费者模型与信号量(条件变量)

一.生产者-消费者模型

        生产者-消费者模型是一个十分经典的多线程并发协作模式。所谓的生产者-消费者,实际上包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据为了解耦生产者和消费者的关系,通常会采用共享的数据区域(临界区)。就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中获取数据,不需要关心生产者的行为。举一个简单的例子,在网络通信过程中消费者可以是多个数据写入线程,负责向输入缓冲区写入以太网数据。消费者为多个数据处理线程,负责网络数据解析与处理。需要注意,这里的生产者与消费者是两类线程,实现了数据收发与数据处理的解耦合。好处一方面在于某一线程的错误不会导致整体传输-解析架构的崩溃,另一方面是可以使得整体程序结构变得清晰明了利于后期维护。

二.实现方式(条件变量)

        std::conditon_variable(c11)在头文件<condition_variable>定义,它是与std::mutex一起使用的同步原语。用于阻塞一个线程或者同时阻塞多个线程,直至另一个线程修改共享条件变量并通知。阻塞多个线程等待通知有利于减少CPU负载,特别是在多个线程的情况下。举个例子:生产者线程修改输入缓存区后,通知消费者线程进行处理。消费者线程在没有接到通知时,一直处在阻塞等待通知的状态,对比死循环加条件判断的形式更节省OS资源消耗。

有意修改条件变量状态的线程需要注意:

1.必须获得std::mutex所有权。(上锁)

2.在保有锁的时候进行修改操作。

3.在std::condition_variable上执行notify_one或notify_all(释放锁后通知)。

任何有意在std::condition_variable上等待的线程需要注意:

1.在用于保护共享变量的互斥体上获得std::unique_lock<std::mutex>。

2.执行检查:

        1.检查条件。

        2.调用wait,wait_for,wait_until。

        3.检查条件,并在为满足的条件下继续阻塞等待。

示例:与mutex同步实现进程间通信

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;void worker_thread()
{// 等待直至 main() 发送数据std::unique_lock lk(m);cv.wait(lk, []{ return ready; });// 等待后,我们占有锁std::cout << "工作线程正在处理数据\n";data += "(处理后)";// 发送数据回 main()processed = true;std::cout << "工作线程指示数据已经处理完成\n";// 通知前完成手动解锁,以避免等待线程才被唤醒就阻塞(细节见 notify_one)lk.unlock();cv.notify_one();
}int main()
{std::thread worker(worker_thread);data = "数据样例";// 发送数据到 worker 线程{std::lock_guard lk(m);ready = true;std::cout << "main() 指示数据已准备好进行处理\n";}cv.notify_one();// 等候 worker{std::unique_lock lk(m);cv.wait(lk, []{ return processed; });}std::cout << "返回 main(),data = " << data << '\n';worker.join();
}

需注意,在通知前先释放锁,不然会出现子线程唤醒后再次阻塞的风险(假通知)。这是一种常见的条件竞争。

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <thread>
using namespace std::chrono_literals;std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;void waits()
{std::unique_lock<std::mutex> lk(cv_m);std::cout << "等待... \n";cv.wait(lk, []{ return i == 1; });std::cout << "...结束等待; i == " << i << '\n';done = true;
}void signals()
{std::this_thread::sleep_for(200ms);std::cout << "假通知...\n";cv.notify_one(); // 等待线程被通知且 i == 0。// cv.wait 被唤醒,检查 i,再回到等待std::unique_lock<std::mutex> lk(cv_m);i = 1;while (!done) {std::cout << "真的改动通知...\n";lk.unlock();cv.notify_one(); // 等待线程被通知且 i == 1,cv.wait 返回std::this_thread::sleep_for(300ms);lk.lock();}
}int main()
{std::thread t1(waits), t2(signals);t1.join(); t2.join();
}

三.使用条件变量实现简单的多线程通信示例

#include <iostream>
#include <string>
#include <sstream>
#include <list>
#include <thread>
#include <condition_variable>
#include <mutex>std::mutex mux;
std::condition_variable cv;
std::list<std::string> buffer;void thRead(int i){for (;;) {std::unique_lock<std::mutex> lock(mux);cv.wait(lock, [](){ return !buffer.empty(); });  //wait解锁阻塞等待唤醒,上锁判断Lambda//如果Lambda返回true获得锁进入处理逻辑while (!buffer.empty()){std::cout << "Read Thread " << i << "\n" << buffer.front() << std::endl;buffer.pop_front();}}
}
void thWrite(){for (int i = 0; ; ++i) {std::unique_lock<std::mutex> lock(mux); //获取锁std::stringstream ss;ss << "Write " << i + 1 << " Times.";buffer.push_back(ss.str());lock.unlock();//先解锁再进行通知,防止读线程死锁cv.notify_one();std::this_thread::sleep_for(std::chrono::seconds(1));   //1s写入一次数据}
}int main() {std::thread write(thWrite);write.detach();for (int i = 0; i < 3; ++i) {std::thread th(thRead, i + 1);th.detach();}getchar();return 0;
}

四.通过信号量通知线程关闭

//
// Created by zty19 on 24-6-3.
//
#include "xmessage.h"
#include <iostream>void XMessage::stop(){exit_flag_ = true;cv.notify_all();wait();
}void XMessage::send(const std::string &msg) {std::unique_lock<std::mutex> lock(buffer_mutex_);send_buffer_.push_back(std::move(msg));lock.unlock();cv.notify_one(); //发布唤醒标志
}void XMessage::Main() {while (!is_exit()) {std::unique_lock<std::mutex> lock(buffer_mutex_);//std::this_thread::sleep_for(std::chrono::microseconds(100));cv.wait(lock, [this](){if (is_exit()) return true;return !send_buffer_.empty(); });if (is_exit()) break;while (!send_buffer_.empty()){std::string tmp = send_buffer_.front();send_buffer_.pop_front();std::cout << tmp << std::endl;}}
}

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

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

相关文章

Opencv 色彩空间

一 核心知识 色彩空间变换&#xff1b; 像素访问&#xff1b; 矩阵的、-、*、、&#xff1b; 基本图形的绘制 二 颜色空间 RGB&#xff1a;人眼的色彩空间&#xff1b; OpenCV默认使用BGR&#xff1b; HSV/HSB/HSL; YUV(视频); 1 RGB 2 BGR 图像的多种属性 1 访问图像(Ma…

人工智能大模型的进化之路:探索如何让它们变得更“聪明”

一、引言 在人工智能&#xff08;AI&#xff09;领域&#xff0c;大模型凭借其强大的处理能力和广泛的应用前景&#xff0c;已经成为研究的热点。然而&#xff0c;尽管这些模型在多个领域展现出了惊人的能力&#xff0c;但它们仍然面临着理解力、泛化能力和适应性等方面的挑战…

学习Java的日子 Day51 数据库,DDL,DML

Day51 MySQL 1.数据库 数据库&#xff08;database&#xff09;就是一个存储数据的仓库。为了方便数据的存储和管理&#xff0c;它将数据按照特定的规律存储在磁盘上。通过数据库管理系统&#xff0c;可以有效地组织和管理存储在数据库中的数据 MySQL就是数据库管理系统&#…

VisionPro界面乱序或字体排版异常,字体不适应界面窗口大小

很多人在安装Visionpro后都遇到了一个界面显示异常的问题&#xff1a; 打开visionpro后字体大小不统一,显示不全或显示在其他窗口之上&#xff0c;如下所示。 解决该问题&#xff0c;首先关闭Visionpro软件&#xff0c;右击软件选择属性->兼容性。勾选兼容模式下面的方框。…

WebStorm 2024.1.1 Mac激活码 前端开发工具集成开发环境(IDE)

WebStorm 2024 Mac激活码 搜索Mac软件之家下载WebStorm 2024 Mac激活版 WebStorm 2024 功能介绍 WebStorm 2024是由JetBrains公司开发的一款专为前端开发设计的集成开发环境&#xff08;IDE&#xff09;。它提供了一整套功能&#xff0c;旨在提高Web开发者的工作效率和代码质…

线性电源运放驱动调整管的方案仿真

群里有人的电路板做出来电压不稳&#xff0c;加负载就掉电压。我对这个运放的工作状态不是很理解&#xff0c;所以仿真了一下。结果却是稳定的。他用12v给运放供电&#xff0c;要求输出10.5. 从仿真看。12运放供电只能输出9v。而且还是到了运放的极限。所以通过仿真后确定怀疑路…

LLM的基础模型4:初识Embeddings

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

软件安全测评之漏洞扫描、代码审计详情介绍

在当今数字化时代&#xff0c;软件已渗透到我们生活的方方面面。然而&#xff0c;与软件的广泛应用相伴随的是各种安全威胁的出现。为了保障用户和企业的信息安全&#xff0c;软件安全测评变得至关重要。而漏洞扫描和代码审计作为安全测评中的重要过程&#xff0c;卓码测评小编…

【线性代数】SVDPCA

用最直观的方式告诉你&#xff1a;什么是主成分分析PCA_哔哩哔哩_bilibili 奇异值分解singular value decomposition&#xff0c;SVD principal component analysis,PCA 降维操作 pca就是降维后使得信息损失最小 投影在坐标轴上的点越分散&#xff0c;信息保留越多 pca的实现…

2-异常-FileNotFoundException(三种不同的报错)

2-异常-FileNotFoundException(三种不同的报错) 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯技术文&#xff09; 生活公众…

关于认证协议

本地用户认证 本地认证的意思就是&#xff0c;我们的电脑上存储着自己的账号密码&#xff0c;无论电脑是否联网&#xff0c;只要能开机&#xff0c;就可以输入账号密码登录到电脑中&#xff0c;工作组就是采用本地认证 本地认证流程 winlogon.exe -> 接收用户输入 -> …

【异常分析:四分位距与3σ原则】

文章目录 前言四分位距&#xff08;IQR&#xff09;3σ原则使用步骤计算四分位距应用3σ原则 代码 前言 异常分析的目标是识别数据中的异常值&#xff0c;这些异常值可能是由于错误的记录、设备故障或者其他未知原因导致的。四分位距&#xff08;interquartile range, IQR&…

H5进度条样式,自定义进度条

进度条样式预览 实现代码&#xff1a; <view class"mainPro"><view class"proBg"><view class"proDetail" :style"{ width: ${schedule}% }"></view></view><view class"proTxt">完成进…

【StableDiffusion】2024.6.4 亲测成功,无魔法 Civitai 镜像,国内下载 Civitai 模型的方法

一、废话不说&#xff0c;直接开始 废话&#xff1a;请注意&#xff0c;这个插件不是万能的&#xff0c;有一些模型无法下载&#xff0c;大概能下载 70% 左右的模型 1.github下载插件 https://github.com/tzwm/sd-webui-model-downloader-cn/tree/main 这个步骤不用我多说了…

Spring boot集成通义千问大模型实现智能问答

Spring boot集成通义千问大模型实现智能问答 背景 我在用idea进行java开发时发现了通义灵码这款免费的智能代码补全插件&#xff0c;用了一段时间了&#xff0c;感觉很不错。就想着在自己的项目中也能集成通义千问大模型实现智能回答&#xff0c;毕竟对接openai需要解决网络问…

SQL注入-时间盲注

SQL时间盲注&#xff08;Time-based Blind SQL Injection&#xff09;&#xff0c;又叫延时注入&#xff0c;是一种SQL注入攻击技术&#xff0c;用于在无法直接获取查询结果或查看响应内容变化的情况下&#xff0c;通过引入时间延迟来推断数据库的信息&#xff1b;时间盲注依赖…

什么是真正的高效阅读,高效阅读的方法和技巧

一、教程描述 查理芒格说他认识的厉害的人没有一个不读书的&#xff0c;为什么我们也读书却成不了厉害的那个人呢&#xff1f;所以这绝对不是书的问题&#xff0c;而是人的问题。阅读应该带有目的性&#xff0c;要帮我们解决实际问题。如果读一本书只是读完它&#xff0c;那读…

如何理解与学习数学分析——第一部分——数学分析概观

第1 部分&#xff1a;数学分析概观(Studying Analysis) 1. 数学分析之面目(What is Analysis like?) 本章说明了分析中的定义、定理和证明。 它介绍了一些符号&#xff0c;并解释了如何使用数学分析中的这些数学符号和数学词汇、以及应该把它们读成什么。它指出了这种类型的…

CANDela studio的State

State主要用来查看&#xff0c;点击State Groups&#xff0c;可以看到session和security下面有多少个会话和security level&#xff0c;所以删除和新建都不能在这里操作。 Dependencies没有安装插件&#xff0c;看不到图形不要紧&#xff0c;点击下面那个图标&#xff0c;就能编…

【好物推荐】夏日肌肤守护者:护肤皂

随着夏日的到来&#xff0c;高温、潮湿和紫外线成为了肌肤的三大挑战。在这个季节里&#xff0c;护肤不仅仅是为了美观&#xff0c;更是对肌肤健康的一种保护。在众多护肤产品中&#xff0c;护肤皂因其清洁力强、使用方便等特点&#xff0c;成为了夏季护肤的得力助手。今天&…