1. 什么是线程同步

为什么会有线程同步,那一定是有了新问题。互斥可以解决临界资源被同时访问的问题,但是纯互斥也会带来新的问题。由于当前被执行的线程离cpu最近【其他线程被阻塞挂起还要被唤醒】,所以,当前进程对于竞争锁天然就有极大的优势。这势必会导致当前线程重复申请和释放锁,其他线程很难拿到锁,也就会造成线程饥饿的问题。纯粹的互斥,不高效,也不公平!

因此,我们提出新的要求,当前线程一旦释放锁就不能立即申请锁,外面的线程也不能乱作一团的竞争锁,必须排队申请锁。而刚释放锁的线程要想再次申请锁,就必须排到队列的末尾!!

我们把多个执行流在临界区安全的前提下,按照顺序依次访问临界资源叫做线程同步!

 2. 条件变量

> 理解条件变量

• 当⼀个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。

• 例如⼀个线程访问队列时,发现队列为空,它只能等待,直到其它线程将⼀个节点添加到队列 中。这种情况就需要用到条件变量

为什么需要用到条件变量呢??因为线程a需要访问队列,线程b添加节点。这两个线程互相竞争锁,但是,只有队列中添加节点后【即满足一定的条件】,线程a拿到锁才能做有效动作。因此,我们引入了条件变量。

当每个线程a访问队列时发现并不满足条件,那么就挂起到队列中等待。当线程b将节点添加之后,满足了条件变量,此时再唤醒等待队列中的一个或全部线程让他们来访问临界资源。这样做就高效多了!

> 条件变量接口

定义和初始化条件变量和互斥锁的使用和互斥锁一样。

 这一批接口就是线程在指定条件变量下等待,很好理解。

唤醒在指定条件变量下等待的一个或所有线程。

> 使用条件变量接口demon 

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>const int num = 5; // 创建5个线程
int cnt = 0;// 定义初始化锁和条件变量
pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;void *routine(void *args)
{std::string name = static_cast<char *>(args);while (true){// 加锁pthread_mutex_lock(&glock);// 使用条件变量等待pthread_cond_wait(&gcond, &glock);std::cout << name << "计算" << cnt << std::endl;cnt++;// 解锁pthread_mutex_unlock(&glock);}return nullptr;
}int main()
{std::vector<pthread_t> tids;// 创建5个线程for (int i = 0; i < num; i++){char *name = new char[64];snprintf(name, 64, "线程%d", i);pthread_t tid;pthread_create(&tid, nullptr, routine, name);tids.push_back(tid);// 每个1秒创建一个线程sleep(1);}// 主线程每隔一秒主动唤醒线程while (true){std::cout << "唤醒线程" << std::endl;// pthread_cond_signal(&gcond);//每隔一秒主动唤醒一个线程pthread_cond_broadcast(&gcond); // 每隔一秒主动唤醒所有线程sleep(1);}for (auto &e : tids){pthread_join(e, nullptr);}return 0;
}

每次唤醒一个线程: 线程依次被唤醒在队列中同步串行运行。

 每次唤醒所有线程:所有线程竞争锁。

3. 生产者消费者模型 

在现实生活中,我们不是直接去工厂购物,而是在超市中购物,因为我们直接去工厂购物成本高,效率低。 

在生产者消费者模型中,一共有3种关系,2种角色,1个交易场所。【321原则】

三种关系:消费者和消费者之间【互斥竞争关系】,生产者和生产者之间【互斥竞争关系】,生产者和消费者之间【互斥和同步关系】。

两种角色:生产者和消费者角色【由线程承担】。

一个交易场所:以特定结构构成的一种”内存“空间。

为什么要有生产者消费者模型呢?? 

1. 生产过程和消费过程解耦。【由于互斥和同步机制,生产和消费之间是不互相干扰的】

2. 支持忙闲不均。【即便消费者线程不工作,生产者线程也可以不断执行。反之也是如此】

3. 提高效率。【获取任务和处理任务是并发的!!】

4. 基于阻塞队列的生产者消费者模型

在多线程编程中阻塞队列(Blocking Queue)是⼀种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)

block_queue.hpp

#pragma once#include <iostream>
#include <pthread.h>
#include <queue>const int defaultcap = 5;template <typename T>
class block_queue
{bool is_empty(){return _q.size() <= 0;}bool is_full(){return _q.size() >= _cap;}public:block_queue(int num = defaultcap): _cap(num), _consum_sleep_num(0), _product_sleep_num(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_full_cond, nullptr);pthread_cond_init(&_empty_cond, nullptr);}// 消费T &consum(){pthread_mutex_lock(&_mutex);while (is_empty()){// 空了就在阻塞队列等待被唤醒_consum_sleep_num++;pthread_cond_wait(&_empty_cond, &_mutex);_consum_sleep_num--; // 被唤醒}T &out = _q.front();_q.pop();// 队列一定不满,唤醒生产者if (_product_sleep_num != 0){std::cout << "唤醒生产者……" << std::endl;pthread_cond_signal(&_full_cond);}// 解锁pthread_mutex_unlock(&_mutex);return out;}// 生产void product(const T &in){pthread_mutex_lock(&_mutex);while (is_full()){// 满了就在阻塞队列等待被唤醒_product_sleep_num++;pthread_cond_wait(&_full_cond, &_mutex);_product_sleep_num--; // 被唤醒}_q.push(in);// 队列一定不空,唤醒消费者if (_consum_sleep_num != 0){std::cout << "唤醒消费者……" << std::endl;pthread_cond_signal(&_empty_cond);}// 解锁pthread_mutex_unlock(&_mutex);}~block_queue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_full_cond);pthread_cond_destroy(&_empty_cond);}private:std::queue<T> _q;int _cap; // 队列容量上限pthread_mutex_t _mutex;pthread_cond_t _full_cond;pthread_cond_t _empty_cond;int _consum_sleep_num;  // 消费者休眠个数int _product_sleep_num; // 生产者休眠个数
};

消费者这里为什么要写成循环呢?? 假设生产者只生产了一个商品,但是生产者却唤醒了一批消费者!!那么消费者就全部来竞争锁,一个消费者消费完一个商品后释放锁。其他消费者并不在阻塞队列中等待,而是在锁上等待。如果消费者立即拿到锁,生产者还没有来得及生产,那么消费者就没有商品可以消费了,但是此时消费者已近持有锁区消费空队列了,也就是_q.pop()。这样在代码层面就出问题了!!我们把这种情况称为伪唤醒所以我们要在判断上加上循环,即便被唤醒了,也要再次检查队列中商品是否为空

main.cc

#include "block_queue.hpp"
#include <unistd.h>// 生产者
void *product(void *args)
{int cnt = 1;block_queue<int> *bq = static_cast<block_queue<int> *>(args);while (true){sleep(1);bq->product(cnt);std::cout << "生产者生产了:" << cnt << std::endl;cnt++;}
}// 消费者
void *consum(void *args)
{block_queue<int> *bq = static_cast<block_queue<int> *>(args);while (true){//sleep(1);int t = bq->consum();std::cout << "生产者消费了:" << t << std::endl;}
}int main()
{// 申请阻塞队列block_queue<int> *bq = new block_queue<int>();// 构建生产者和消费者pthread_t p, c;pthread_create(&p, nullptr, product, bq);pthread_create(&p, nullptr, consum, bq);pthread_join(p, nullptr);pthread_join(c, nullptr);return 0;
}

5. 封装条件变量 

mutex.hpp

#pragma once
#include <iostream>
#include <pthread.h>
namespace mutex_module
{class mutex{public:mutex(){int n = pthread_mutex_init(&_lock, nullptr);(void)n;}void lock(){pthread_mutex_lock(&_lock);}void unlock(){pthread_mutex_unlock(&_lock);}~mutex(){int n = pthread_mutex_destroy(&_lock);}pthread_mutex_t* get(){return &_lock;}private:pthread_mutex_t _lock;};// 采⽤RAII⻛格,进⾏锁管理class lock_guard{public:lock_guard(mutex &m) : _m(m){_m.lock();}~lock_guard(){_m.unlock();}private:mutex &_m;};
}

cond.hpp

#pragma once
#include <iostream>
#include <pthread.h>
#include "mutex.hpp"using namespace mutex_module;namespace cond_module
{class cond{public:cond(){pthread_cond_init(&_cond, nullptr);}void wait(mutex &lock){pthread_cond_wait(&_cond, lock.get());}void signal(){pthread_cond_signal(&_cond);}void broadcast(){pthread_cond_broadcast(&_cond);}~cond(){pthread_cond_destroy(&_cond);}private:pthread_cond_t _cond;};
}

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

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

相关文章

基于arduino uno r3主控的环境监测系统设计-1

准备设计arduino uno r3为主控的环境监测系统&#xff0c;通过传感器采集TVOC&#xff08;总挥发性有机物&#xff09;、HCHO&#xff08;甲醛&#xff09;和eCO2&#xff08;等效二氧化碳&#xff09;数据&#xff0c;并显示在LCD屏幕上&#xff0c;同时支持数据记录到SD卡&am…

ITIL 4:云计算与微服务对组织架构的影响

这几年&#xff0c;很多组织在推进数字化转型时遇到一个共同的问题&#xff1a;业务节奏越来越快&#xff0c;但内部协作的“架构”却越来越跟不上节奏。技术架构的变革&#xff0c;必须同步推动组织架构的重塑。特别是随着云计算和微服务架构的广泛应用&#xff0c;这种影响愈…

【Android】xml和Java两种方式实现发送邮件页面

三三要成为安卓糕手 一&#xff1a;xml中LinearLayout布局参数的使用 1&#xff1a;xml代码 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http:/…

美林数据用大模型重构电能质量评估,让隐蔽合规问题无所遁形

在“双碳”目标驱动下&#xff0c;电网企业正加速推进数字化转型&#xff0c;电能质量评估作为电力系统安全运行的核心环节&#xff0c;其合规性与效率直接影响着电网智能化水平。然而&#xff0c;传统人工审核模式已难以应对海量报告与复杂标准——单份报告需20-30人天核对、关…

前端基础 JS Vue3 Ajax

一、JSalert( .... ) //弹出框console.log( ....... ) //输出到控制台浏览器JS引入方式&#xff1a;1、内部脚本&#xff1a;将JS代码定义在HTML页面中位于<script></script>标签之间2、外部脚本&#xff1a;将JS代码写在外部JS文件中&#xff0c;在HTML页面中使用…

如何解决pip安装报错ModuleNotFoundError: No module named ‘notebook’问题

【Python系列Bug修复PyCharm控制台pip install报错】如何解决pip安装报错ModuleNotFoundError: No module named ‘notebook’问题 一、摘要 在使用 PyCharm 进行 Python 开发时&#xff0c;常常需要通过 pip install 安装第三方包。但有时即便已经安装成功&#xff0c;运行代…

一、Vue概述以及快速入门

什么是VueVue的快速入门代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Vue快速入门</title><script src"js/vue.js"></script> </head> <bod…

模型的存储、加载和部署

定义损失函数并以此训练和评估模型 存储模型可以只存储state_dict或模型参数&#xff0c;每当需要部署经过训练的模型时&#xff0c;创建模型的对象并从文件中加载参数&#xff0c;这是 Pytorch 创建者推荐的方法。 目录 模型的存储、加载 模型的部署 模型的存储、加载 承接…

Java学习第七十部分——微服务架构

目录 一、前言提要 二、核心优势 三、核心技术栈 四、构建步骤 五、困难挑战 六、总结归纳 一、前言提要 Java 微服务架构是一种使用 Java 技术栈构建分布式系统的方法论&#xff0c;它将单一的大型应用程序分解为一组小型、独立、松耦合、可独立部署和扩展的服务。每个服…

六边形滚动机器人cad【7张】三维图+设计书明说

摘 要 机械制造业是国家的重要产业,随着时代的发展,智能化越来越在生活中变得普遍,工业的发展深深的影响着一个国家的经济发展。全球经济的发展带领着机械工业在不断的进步。随着国外先进技术在我国的传播,也影响着我国技术的发展,在全球经济的大环境的推动下,大型四边形…

人形机器人加快先进AI机器人开发

物理AI的新时代通用人形机器人专为快速适应现有的以人类为中心的城市和工业工作空间而构建&#xff0c;用以承担枯燥、重复性或对体力要求高的工作任务。这些机器人正在从工厂车间走向医疗健康机构&#xff0c;通过自动化帮助人类工作&#xff0c;缓解劳动力短缺问题。但是&…

AI 驱动开发效能跃升:企业级智能开发全流程优化方案​

企业软件开发正面临 “三高困境”&#xff1a;需求变更频率高、人力成本占比高、线上故障风险高。破解这些难题的核心在于构建 AI 驱动的全流程智能开发体系&#xff0c;通过系统化效能优化实现开发能力升级。​ 需求分析作为开发起点&#xff0c;常因理解偏差导致后期返工。A…

时序数据库 TDengine × Ontop:三步构建你的时序知识图谱

在做设备预测性维护或能源管理分析时&#xff0c;你是否也曾思考过&#xff1a;如何才能让机器“理解”我们收集的大量时序数据&#xff1f;工业现场的数据是结构化的&#xff0c;而语义分析、知识推理却往往需要 RDF 等图谱格式。换句话说&#xff0c;“会说话”的数据更聪明&…

Android启动图不拉伸且宽占满屏幕

Android启动图不拉伸且宽占满屏幕 一般启动图的做法&#xff1a; start_app_bg.xml <?xml version"1.0" encoding"utf-8"?> <layer-list xmlns:android"http://schemas.android.com/apk/res/android"><item><shape>&l…

rust-方法语法

方法语法 方法类似于函数&#xff1a;我们用 fn 关键字和一个名称来声明它们&#xff0c;它们可以有参数和返回值&#xff0c;并且包含一些在从其他地方调用该方法时运行的代码。与函数不同&#xff0c;方法是在结构体&#xff08;或枚举、trait 对象&#xff0c;分别在第6章和…

【C++】C++ 的入门语法知识1

本文主要讲解C语言的入门知识&#xff0c;包括命名空间、C的输入与输出、缺省参数以及函数重载。 目录 1 C的第一个程序 2 命名空间 1&#xff09; 命名空间存在的意义 2&#xff09; 命名空间的定义 3&#xff09; 命名空间的使用 3 C的输出与输入 1&#xff09; C中…

SpringBoot6-10(黑马)

JWT令牌简介&#xff1a;1.JWT全称:JSON Web Token(https://iwt.io/)定义了一种简洁的、自包含的格式&#xff0c;用于通信双方以json数据格式安全的传输信息。2.组成: >第一部分:Header(头)&#xff0c;记录令牌类型、签名算法等。例如:("alg":“HS256",“t…

智能制造场景195个术语的16个分类

说明&#xff1a;《智能制造典型场景参考指引&#xff08;2025年版&#xff09;》日前&#xff0c;由工信部办公厅正式发布&#xff0c;将成为众多制造型企业的工作纲领 1. 工厂数字化规划设计&#xff08;1.1&#xff09;&#xff1a;在电脑上用专业软件设计工厂布局、规划生产…

[论文阅读] 人工智能 + 软件工程 | 微信闭源代码库中的RAG代码补全:揭秘工业级场景下的检索增强生成技术

微信闭源代码库中的RAG代码补全&#xff1a;揭秘工业级场景下的检索增强生成技术 论文标题&#xff1a;A Deep Dive into Retrieval-Augmented Generation for Code Completion: Experience on WeChatarXiv:2507.18515 A Deep Dive into Retrieval-Augmented Generation for Co…

RabbitMQ—仲裁队列

上篇文章&#xff1a; RabbitMQ集群搭建https://blog.csdn.net/sniper_fandc/article/details/149312481?fromshareblogdetail&sharetypeblogdetail&sharerId149312481&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目录 1 Raft一致性算法…