网站开发外包/西安官网seo

网站开发外包,西安官网seo,常州制作网站信息,做企业网站要多长时间📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…

📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 📢前言
  • 🏳️‍🌈一、线程同步概念
  • 🏳️‍🌈二、为什么需要线程同步?
    • 2.1 防止数据竞争(Data Race)​
    • 2.2 保证操作的原子性
    • 2.​3 协调线程间的执行顺序
    • 2.​4 避免资源争用(如文件、网络连接)​
  • 🏳️‍🌈线程同步的常见手段
  • 🏳️‍🌈三、什么是生产消费者模型
    • 3.1 三种关系
    • 3.2 两个角色
    • 3.3 一个场景
  • 🏳️‍🌈四、以阻塞队列模拟多生产消费者模型
    • 4.1 成员名
    • 4.2 构造函数和析构函数
    • 4.3 模拟的生产者和消费者
    • 4.4 模拟生产、消费者过程
  • 🏳️‍🌈五、整体代码
    • 5.1 BlockQueue.hpp
    • 5.2 Main.cc
    • 5.3 Makefile
  • 👥总结


📢前言

紧接上一回的 从互斥原理到C++ RAII封装实践 笔者这回介绍一下线程中几乎与互斥一样重要的同步原理,

还有一点,笔者之后的封装都会使用之前博客中封装好的容器,需要的可以去仓库或者前面的博客中自取。

所需所用的都放在了这个仓库中
在这里插入图片描述


🏳️‍🌈一、线程同步概念

条件变量:当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

线程同步 指在多线程编程中,通过特定机制协调多个线程的执行顺序,确保它们对共享资源​(如内存、文件、硬件等)的访问安全有序。核心目标是防止并发访问导致的数据混乱、逻辑错误或资源冲突

🏳️‍🌈二、为什么需要线程同步?

2.1 防止数据竞争(Data Race)​

​问题:多个线程同时读写共享数据时,执行顺序不确定,可能导致数据不一致。

int balance = 100;  // 共享变量// 线程A执行:存入200
balance += 200;  // 线程B执行:取出150
balance -= 150;
  • 未同步时:若线程A和B同时读取balance=100,最终结果可能是100+200-150=150(正确应为150)或100-150+200=150,但若操作交叉执行(如A读后B写),可能得到错误值(如-50)。

2.2 保证操作的原子性

​问题:单个操作(如i++)在底层可能对应多条机器指令,线程切换会导致操作未完成就被中断

; x86的i++实际步骤:
mov eax, [i]  ; 读取i到寄存器
inc eax       ; 寄存器加1
mov [i], eax  ; 写回内存
  • 若线程A执行到inc eax后被切换,线程B修改了i,线程A恢复后会将旧值写回,导致结果错误。

2.​3 协调线程间的执行顺序

​场景:某些任务需要线程按特定顺序执行。
​生产者-消费者模型:消费者线程需等待生产者生成数据后再读取。
​任务依赖:线程B必须在线程A完成初始化后才能执行。

2.​4 避免资源争用(如文件、网络连接)​

​问题:多个线程同时写入同一文件或占用同一网络端口,会导致数据错乱或程序崩溃。

🏳️‍🌈线程同步的常见手段

在这里插入图片描述
同步问题的严重后果
​数据不一致:程序输出错误,如银行账户余额异常。
程序崩溃:多线程同时释放内存导致双重释放(Double Free)。
死锁(Deadlock)​:线程互相等待对方释放锁,导致永久阻塞。

说白了,线程同步就是一种为了统一管理生产消费者模型的一种机制

🏳️‍🌈三、什么是生产消费者模型

在这里插入图片描述

3.1 三种关系

在这里插入图片描述

3.2 两个角色

生产者,模拟是同数据的那方
消费者,取走数据的那方

3.3 一个场景

所有生产消费所用的数据都是在中间的 “超市” 中进行
在这里插入图片描述

🏳️‍🌈四、以阻塞队列模拟多生产消费者模型

下图是以阻塞队列模拟多生产消费者模型的基本过程

也就是有两类线程(生产者和消费者),从同一个场景(blockqueue)中放入和拿出数据的过程
在这里插入图片描述

4.1 成员名

我们利用现有的库函数对环境进行一下封装,再利用一个队列模拟临界资源

    private:std::queue<T> _q;               // 保存数据的容器,临界资源int _cap;                       // bq最大容量pthread_mutex_t _mutex;         // 互斥pthread_cond_t _productor_cond; // 生产者条件变量pthread_cond_t _consumer_cond;  // 消费者条件变量int _cwait_num;                 // 当前等待的消费者数量int _pwait_num;                 // 当前等待的生产者数量};

4.2 构造函数和析构函数

		BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0){pthread_mutex_init(&_mutex, nullptr);         // 创建互斥锁pthread_cond_init(&_productor_cond, nullptr); // 生产者条件变量pthread_cond_init(&_consumer_cond, nullptr);  // 消费者条件变量}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_productor_cond);pthread_cond_destroy(&_consumer_cond);}

4.3 模拟的生产者和消费者

void Equeue(const T &in) // 生产者{pthread_mutex_lock(&_mutex);// 你想放数据,就能放数据吗??生产数据是有条件的!// 结论1: 在临界区中等待是必然的(目前)while (IsFull()) // 5. 对条件进行判断,为了防止伪唤醒,我们通常使用while进行判断!{std::cout << "生产者进入等待..." << std::endl;// 2. 等是,释放_mutex_pwait_num++;pthread_cond_wait(&_productor_cond, &_mutex); // wait的时候,必定是持有锁的!!是有问题的!_pwait_num--;// 3. 返回,线程被唤醒&&重新申请并持有锁(它会在临界区内醒来!)std::cout << "生产者被唤醒..." << std::endl;}// 4. if(IsFull())不满足 || 线程被唤醒_q.push(in); // 生产// 肯定有数据!if(_cwait_num){std::cout << "叫醒消费者" << std::endl;pthread_cond_signal(&_consumer_cond);}pthread_mutex_unlock(&_mutex);}void Pop(T *out) // 消费者{pthread_mutex_lock(&_mutex);while(IsEmpty()){std::cout << "消费者进入等待..." << std::endl;_cwait_num++;pthread_cond_wait(&_consumer_cond, &_mutex); // 伪唤醒_cwait_num--;std::cout << "消费者被唤醒..." << std::endl;}// 4. if(IsEmpty())不满足 || 线程被唤醒*out = _q.front();_q.pop();// 肯定有空间if(_pwait_num){std::cout << "叫醒生产者" << std::endl;pthread_cond_signal(&_productor_cond);}pthread_mutex_unlock(&_mutex);}

4.4 模拟生产、消费者过程

我们假设生产速度小于消费速度,相当于我们没生产一个对象后需要花费一定的时间,但是消费者一直就绪,就要等生产者生产出来

void *Consumer(void *args)
{BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);while(true){int data;// 1. 从bq拿到数据bq->Pop(&data);// 2.做处理printf("Consumer, 消费了一个数据: %d\n", data);}
}void *Productor(void *args)
{BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);int data = 10;while (true){sleep(2);// 1. 从外部获取数据// data = 10; // 有数据???// 2. 生产到bq中bq->Equeue(data);printf("producter 生产了一个数据: %d\n", data);data++;}
}

🏳️‍🌈五、整体代码

5.1 BlockQueue.hpp

#pragma once#include <iostream>
#include <queue>
#include <pthread.h>namespace BlockQueueModule
{static const int gcap = 10;template <typename T>class BlockQueue{private:bool IsFull() { return _q.size() == _cap; }bool IsEmpty() { return _q.empty(); }public:BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0){pthread_mutex_init(&_mutex, nullptr);         // 创建互斥锁pthread_cond_init(&_productor_cond, nullptr); // 生产者条件变量pthread_cond_init(&_consumer_cond, nullptr);  // 消费者条件变量}void Equeue(const T &in) // 生产者{pthread_mutex_lock(&_mutex);// 你想放数据,就能放数据吗??生产数据是有条件的!// 结论1: 在临界区中等待是必然的(目前)while (IsFull()) // 5. 对条件进行判断,为了防止伪唤醒,我们通常使用while进行判断!{std::cout << "生产者进入等待..." << std::endl;// 2. 等是,释放_mutex_pwait_num++;pthread_cond_wait(&_productor_cond, &_mutex); // wait的时候,必定是持有锁的!!是有问题的!_pwait_num--;// 3. 返回,线程被唤醒&&重新申请并持有锁(它会在临界区内醒来!)std::cout << "生产者被唤醒..." << std::endl;}// 4. if(IsFull())不满足 || 线程被唤醒_q.push(in); // 生产// 肯定有数据!if(_cwait_num){std::cout << "叫醒消费者" << std::endl;pthread_cond_signal(&_consumer_cond);}pthread_mutex_unlock(&_mutex);}void Pop(T *out) // 消费者{pthread_mutex_lock(&_mutex);while(IsEmpty()){std::cout << "消费者进入等待..." << std::endl;_cwait_num++;pthread_cond_wait(&_consumer_cond, &_mutex); // 伪唤醒_cwait_num--;std::cout << "消费者被唤醒..." << std::endl;}// 4. if(IsEmpty())不满足 || 线程被唤醒*out = _q.front();_q.pop();// 肯定有空间if(_pwait_num){std::cout << "叫醒生产者" << std::endl;pthread_cond_signal(&_productor_cond);}pthread_mutex_unlock(&_mutex);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_productor_cond);pthread_cond_destroy(&_consumer_cond);}private:std::queue<T> _q;               // 保存数据的容器,临界资源int _cap;                       // bq最大容量pthread_mutex_t _mutex;         // 互斥pthread_cond_t _productor_cond; // 生产者条件变量pthread_cond_t _consumer_cond;  // 消费者条件变量int _cwait_num;                 // 当前等待的消费者数量int _pwait_num;                 // 当前等待的生产者数量};
}

5.2 Main.cc

#include "BlockQueue.hpp"
#include <pthread.h>
#include <unistd.h>using namespace BlockQueueModule;void *Consumer(void *args)
{BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);while(true){int data;// 1. 从bq拿到数据bq->Pop(&data);// 2.做处理printf("Consumer, 消费了一个数据: %d\n", data);}
}void *Productor(void *args)
{BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);int data = 10;while (true){sleep(2);// 1. 从外部获取数据// data = 10; // 有数据???// 2. 生产到bq中bq->Equeue(data);printf("producter 生产了一个数据: %d\n", data);data++;}
}int main()
{// 交易场所,不仅仅可以用来进行传递数据// 传递任务!!!v1: 对象 v2BlockQueue<int> *bq = new BlockQueue<int>(5); // 共享资源 -> 临界资源// 单生产,单消费pthread_t c1, p1, c2, p2, p3;pthread_create(&c1, nullptr, Consumer, bq);pthread_create(&c2, nullptr, Consumer, bq);pthread_create(&p1, nullptr, Productor, bq);pthread_create(&p2, nullptr, Productor, bq);pthread_create(&p3, nullptr, Productor, bq);pthread_join(c1, nullptr);pthread_join(c2, nullptr);pthread_join(p1, nullptr);pthread_join(p2, nullptr);pthread_join(p3, nullptr);delete bq;return 0;
}

5.3 Makefile

bin=bq
cc=g++
src=$(wildcard *.cc)
obj=$(src:.cc=.o)$(bin):$(obj)$(cc) -o $@ $^ -lpthread
%.o:%.cc$(cc) -c $< -std=c++17.PHONY:clean
clean:rm -f $(bin) $(obj).PHONY:test
test:echo $(src)echo $(obj)

👥总结

本篇博文对 同步原理剖析及模拟多消费者模型 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

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

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

相关文章

<数据集>轨道异物识别数据集<目标检测>

数据集下载链接&#xff1a;https://download.csdn.net/download/qq_53332949/90527370 数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1659张 标注数量(xml文件个数)&#xff1a;1659 标注数量(txt文件个数)&#xff1a;1659 标注类别数&#xff1a;6 标注类别…

LabVIEW液压振动锤控制系统

在现代工程机械领域&#xff0c;液压振动锤的高效与精准控制日益显得重要。本文通过LabVIEW软件&#xff0c;展开液压振动锤启停共振控制技术的研究与应用&#xff0c;探讨如何通过改进控制系统来优化液压振动锤的工作性能&#xff0c;确保其在复杂工况下的稳定性与效率。 ​ …

【开源宝藏】30天学会CSS - DAY7 第七课 CSS 关键帧打造Preloader 追逐动画

你的代码实现了一个 方形轨迹预加载动画&#xff08;Preloader Animation&#xff09;&#xff0c;其中三个 span 元素沿着一个 22 网格 轨迹循环移动。现在&#xff0c;我们将 拆解核心实现步骤&#xff0c;让你能一步步理解并调整动画效果。 第 0 步&#xff1a;项目概览 你…

GPT-SoVITS本地部署:低成本实现语音克隆远程生成音频全流程实战

文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 今天要给大家安利一个绝对能让你大呼过瘾的声音黑科技——GPT-SoVITS&#xff01;这款由花儿不哭大佬精心打造的语音克隆神…

JVM(基础篇)

一.初识JVM 1.什么是JVM JVM全称Java Virtyal Machine&#xff0c;中文译名 Java虚拟机 。JVM本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件(将字节码解释成机器码)。 2.JVM的功能 解释和运行&#xff1a;对字节码文件中的指令号&#xff0c;实时…

【高并发内存池】第四弹---深入理解PageCache:整体设计、核心实现及Span获取策略详解

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】【项目详解】 目录 1、pagecache 1.1、整体设计 1.2、核心实现 1.3、获取Span 1.3.1、获取一个非空的Span 1.3…

深入理解C语言数据结构之快速排序三路划分

在数据结构和算法的世界里&#xff0c;排序算法是基石一般的存在。快速排序作为一种高效的排序算法&#xff0c;以其平均情况下的优秀时间复杂度而被广泛应用。今天&#xff0c;让我们深入探讨快速排序的一种变体——三路划分的快速排序&#xff0c;看看它是如何在C语言中施展魔…

Java实现后量子密码(PQC)与国密算法(SM4)混合加密

以下是使用Java实现一种后量子密码(PQC)与国密算法(SM4)混合加密的示例方案。该方案结合了后量子密码的抗量子特性与国密算法的国产化合规要求,适合需要双重安全保障的场景。 一 . 方案验证 1.代码截图 2.运行测试 二 . 方案设计 密钥交换:使用后量子密码(如Kyber)生…

【SQL Server数据库备份详细教程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

SpringBoot古典舞在线交流平台设计与实现

随着古典舞文化的普及&#xff0c;越来越多的人希望通过线上平台交流学习。幽络源作为一站式综合平台&#xff0c;致力于为用户提供免费源码、技术教程及网络兼职资源。本文将详细介绍基于SpringBoot的古典舞在线交流平台的设计与实现&#xff0c;帮助开发者快速搭建一个功能完…

Altium Designer数模电学习笔记

模电 电容 **退耦&#xff1a;**利用通交阻直&#xff0c;将看似直流的信号中的交流成分滤除 &#xff08;一般用在给MPU供电&#xff0c;尽量小一些&#xff0c;10nf~100nf~1uf以下&#xff09; **滤波&#xff1a;**也可以理解为给电容充电&#xff0c;让电容在电平为低时…

光谱仪与光谱相机的核心区别与协同应用

一、核心功能与数据维度 ‌光谱仪‌ ‌功能定位‌&#xff1a;专注单点或线状区域的光谱分析&#xff0c;通过色散元件&#xff08;光栅/棱镜&#xff09;分离波长&#xff0c;生成一维或二维光谱曲线&#xff0c;用于量化光强、吸收率等参数‌。 ‌数据维度‌&#xff1a;输…

Pytorch中layernorm实现详解

平时我们在编写神经网络时&#xff0c;经常会用到layernorm这个函数来加快网络的收敛速度。那layernorm到底在哪个维度上进行归一化的呢&#xff1f; 一、问题描述 首先借用知乎上的一张图&#xff0c;原文写的也非常好&#xff0c;大家有空可以去阅读一下&#xff0c;链接放…

在windows下安装windows+Ubuntu16.04双系统(上)

这篇文章的内容主要来源于这篇文章&#xff0c;给文章很详细的介绍了如何从windows下安装windowsubuntu16.04双系统。我刚开始装双系统都是参照这个方法&#xff0c;该作者前后更新了两个版本&#xff0c;在这里对其稍微进行整理一下。 一、准备&#xff1a;&#xff08;这里推…

如何获取thinkphp的所有发行版本

是的&#xff0c;你只需要一行代码 composer show topthink/think --all 然后做了一个小实验&#xff0c;神奇的事情发生了。是我眼睛花了吗&#xff1f; 命令也能模糊查询了吗&#xff1f;tp6也太。。。。

算法模型从入门到起飞系列——递归(探索自我重复的奇妙之旅)

文章目录 前言一、递归本质1.1 递归的要素1.2 递归特点 二、递归&迭代2.1 递归&迭代比较2.2 递归&迭代如何实现相同功能2.2.1 递归实现2.2.2 迭代实现2.2.3 性能对比 三、优雅的递归理解3.1 阶乘计算分解3.2 [DFS](https://blog.csdn.net/qq_38315952/article/deta…

PostgreSQL_安装

目录 前置&#xff1a; 安装过程&#xff1a; 1 下载软件 2 创建安装文件夹和放置数据的文件夹 3 双击安装 4 连接服务 前置&#xff1a; PostgreSQL 15 windows 10 专业版 安装过程&#xff1a; 1 下载软件 PostgreSQL: Downloads 大小326MB 2 创建安装文件夹和放…

docker desktop 集成WSL Ubuntu22.04

Windows docker desktop 设置WSL ubuntu 22.04启用与其他发行版的集成 Windows docker desktop 安装参考 wsl ubuntu 22.04 查看我宿主机的docker desktop 容器全部的信息 wsl -d Ubuntu-22.04 -u root

从国家能源到浙江交通投资,全息技术在能源交通领域的创新应用

一、3D全息技术行业应用参数及设计制作要求 全息投影 全息投影技术通过激光器、全息片等设备&#xff0c;将物体的三维信息记录下来&#xff0c;并在特定条件下再现。应用参数包括投影距离、投影面积、投影亮度等。设计制作要求&#xff1a;高清晰度、高亮度、低噪音、稳定性好…

新能源汽车充换站如何实现光储充一体化管理?

长三角某换电站光伏板晒到发烫&#xff0c;却因电网限电被迫切机&#xff1b;北京五环充电站每月多缴6万超容费&#xff1b;深圳物流车充电高峰排队3小时...当95%的充换站深陷“用不起绿电、扛不住扩容、算不清碳账”困局&#xff0c;安科瑞用一组真实数据撕开行业潜规则&#…