【linux】线程 (三)
13. 常见锁概念
(一)了解死锁
死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程占有的,且不释放的资源,而处于的一种永久等待状态
(二)死锁四个必要条件
- 互斥条件:一个资源每次只能被一个执行流使用
- 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
- 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
(二)避免死锁
- 破坏死锁的四个必要条件
- 加锁顺序一致 (不是交错申请不同的锁资源)
- 避免锁未释放的场景
- 资源一次性分配
14. linux线程同步
(一)条件变量
当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了,为了使抢夺锁资源更公正(防止饥饿问题),就需要把所有等待锁资源的线程放入一个等待队列,直到线程被唤醒
。这种情况就需要用到条件变量
注意:
条件变量必须依赖锁
(二)同步概念
同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步
(三)条件变量相关函数
条件变量函数 初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);
参数:
cond:要初始化的条件变量
attr:条件变量的属性,一般设置成NULL
销毁
int pthread_cond_destroy(pthread_cond_t *cond)
参数:要销毁的条件变量函数
等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量
注意:
参数中有互斥量,是因为实现同步与互斥,需要先加锁,再判断(判断也是访问临界资源,和加锁保护线程安全对应)是否进入等待队列休眠,而这个函数里面有互斥量是为了把锁资源释放,接下来访问临界资源的顺序是按照队列里面的顺序进行的(前提是线程被唤醒)
大概代码思路
pthread_mutex_lock(&mutex); // 加锁
pthread_cond_wait(&cond); //放入等待队列
pthread_mutex_unlock(&mutex); //解锁
注意:
- 唤醒可以由其它线程去唤醒,如主线程
- 三个函数的顺序不要更换(解锁和等待都不是原子操作)
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
唤醒队列中的所有线程
int pthread_cond_signal(pthread_cond_t *cond);
按顺序唤醒队列中的一个线程
注意:
- 唤醒之后的线程需要申请锁资源
- 唤醒的时候,可能出现唤醒错误的情况。比如在生产者消费者模型中(多生产者多消费者容易出现这种错误),唤醒了多个生成者,并且消费者此时也是属于唤醒状态,导致生产者和生产者之间,消费者和生产者之间竞争同一把锁,锁最后的分配,可能导致临界资源访问条件不满足(唤醒之后拿到锁资源的线程,有可能这个时候的访问条件不满足,但依然会执行后面的代码),出现错误
正确代码思路
pthread_mutex_lock(&mutex); // 加锁
while(... ...) //判断访问资源的条件是否成立
{
pthread_cond_wait(&cond); //放入等待队列
}
pthread_mutex_unlock(&mutex); //解锁
注意:
判断用while,是为了保证,线程被唤醒(出了等待队列),并且竞争到了锁资源后,再一次判断资源条件是否成立,才能决定后面的代码能否执行
15. 生产者消费者模型
(一)使用生产者消费者模型的原因
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯
生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列;消费者不找生产者要数据,而是直接从阻塞队列里取
阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,这个阻塞队列就是用来给生产者和消费者解耦的
(二)生产者消费者模型优点
- 解耦
- 支持并发
- 支持忙闲不均
(三)基于BlockingQueue的生产者消费者模型
BlockingQueue
在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出
(四)对生产者消费者模型的总结
1. 有三种关系
- 生成者和生成者之间的关系:互斥
- 生产者和消费者之间的关系:互斥,同步
- 消费者和消费者之间的关系:互斥
2. 有两者角色
生产者和消费者
3. 有一种交易场所
特定结构的内存空间
16. POSIX信号量
POSIX信号量和 SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步
17. 基于环形队列的生产消费模型
(一)了解基于环形队列的生产消费模型
环形队列采用数组模拟,用模运算来模拟环状特性
判断队列是否为空或者为满,我们交给信号量去处理
注意:
- 信号量的本质是一个计数器,用来描述临界资源的数目(这里不把临界资源当成一个整体,而是很多份,多个线程并发访问临界资源中的不同份)
- 临界资源是否就绪的判断是在临界区之外的(和前一个生产消费模型区分开来),在申请信号量的时候,就已经间接在做判断了(这一步是预定操作,即预定成功,则整个临界资源必有一份资源是属于预定成功的线程的)
(二)基于环形队列的生产消费模型的原理
18. 线程池
(一)了解线程池
线程池:
一种线程使用模式
线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程(提前开辟好的),等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度
池化计数:
线程会用到池化技术(以空间换时间),大致操作(C++为例):现在C++类内创建线程(创建的线程当作类内成员变量),再利用原生线程去实现生产者消费者模型
注意:
- 在类内创建时,若线程执行的函数也是类内函数,会出现错误(因为执行的函数传参只有一个void*,而类内函数会多一个this指针),所以该执行函数必须是静态的
- 若执行函数是静态的,同样会出现一个问题,即静态成员函数无法访问非静态成员变量(没有this指针),所以执行函数的参数void*必须是this指针,通过穿过来的this指针,访问非静态成员变量
(二)线程池的应用场景
- 需要大量的线程来完成任务,且完成任务的时间比较短。因为单个任务小,而任务数量巨大, 但对于长时间的任务,线程池的优点就不明显了
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限
(三)线程池示例
- 创建固定数量线程池,循环从任务队列中获取任务对象
- 获取到任务对象后,执行任务对象中的任务接口
19. 线程安全的单例模式
(一)什么是单例模式
单例模式是一种 "经典的, 常用的, 常考的" 设计模式(这个类的对象,只允许创建一个)
(二)什么是设计模式
针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式
(三)单例模式的特点
- 某些类, 只应该具有一个对象(实例), 就称之为单例
- 在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中. 此时往往要用一个单例的类来管理这些数据
(四)饿汉实现方式和懒汉实现方式
举例类比(洗完的例子)
饿汉方式:
吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭
懒汉方式:
吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式
注意:
懒汉方式最核心的思想是 "延时加载". 从而能够优化服务器的启动速度(在准备进程启动时,需要把一些数据加载到内存,这一个过程的时间少)
饿汉方式 代码实现
template <typename T> class Singleton { static T data; public: static T* GetInstance() { return &data; } };
data 是静态类内成员,会在进程启动之前,早就创建好,而不是等到启动进程时,再去创建
懒汉模式 代码实现
template <typename T> class Singleton { static T* inst; public: static T* GetInstance() { if (inst == nullptr) { inst = new T(); } return inst; } }; template <typename T> T* Singleton<T>:: inst = nullptr;
inst指针是要等待使用的时候,所指向的内容才会在堆上开辟新空间
(五)懒汉方式实现单例模式(线程安全版本)
代码
static phread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; template <typename T> class Singleton { static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化. public: static T* GetInstance() { if (inst == NULL) //这一次的判断可以提高效率,使得没有进入第二个判断的线程,直接执行非临界资源的代码 { lock.lock(); // 使用互斥锁, 防止多个线程进入第二个判断,保证多线程情况下也只调用一次 new if (inst == NULL) { inst = new T(); } lock.unlock(); }return inst; } };T* Singleton<T>:: inst = nullptr;
注意:(代码不是完整的)
lock(),unlock()底层都是用原生线程库封装了
20. STL , 智能指针和线程安全
(一)常见问题
- STL中的容器是否是线程安全
不安全
原因:
STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全, 会对性能造成巨大的影响.
而且对于不同的容器, 加锁方式的不同, 性能可能也不同(例如hash表的锁表和锁桶).
因此 STL 默认不是线程安全. 如果需要在多线程环境下使用, 往往需要调用者自行保证线程安全.
- 智能指针是否是线程安全
- 对于 unique_ptr, 由于只是在当前代码块范围内生效, 因此不涉及线程安全问题.
- 对于 shared_ptr, 多个对象需要共用一个引用计数变量, 所以会存在线程安全问题. 但是标准库实现的时候考虑到了这个问题, 基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数.
(二)其他常见的各种锁
- 悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起
- 乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作
- CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。
- 自旋锁(是否采用自旋锁,取决于临界资源访问的时间的长短,时间短,可以采用自旋锁,即线程不挂起,一直申请所资源)
21. 读者写者问题
读写锁
在编写多线程的时候,有一种情况是十分常见的:有些公共数据修改的机会比较少,相比较改写,它们读的机会反而高的多。
读写锁可以专门处理这种多读少写的情况
注意:写和写竞争,写和读竞争且同步,读和读共享资源,读锁优先级高
相关函数
初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t
*restrict attr);
销毁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加锁和解锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //写与写竞争
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); //读与写竞争
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
代码(部分)
void * reader(void * arg) //读端 { char *id = (char *)arg; while (1) { pthread_rwlock_rdlock(&rwlock); if () //判断 { pthread_rwlock_unlock(&rwlock); break; } pthread_rwlock_unlock(&rwlock); } return nullptr; }void * writer(void * arg) //写端 { char *id = (char *)arg; while (1) { pthread_rwlock_wrlock(&rwlock); if () //判断条件 { pthread_rwlock_unlock(&rwlock); break; }} return nullptr; }
中间一些过程省略了
底层实现(伪代码)
int reader_count = 0; mutex_t rlock,wlock; 读端: lock(&rlock) reader_count++; if(reader_count == 1) //第一个竞争到所资源的读端 {lock(&wlock); //写段不能访问 } unlock(&rlock)lock(&rlock); reader_count--; if(reader_count == 0)//一开始一批读端的最后一个 {unlock(&wlock); //此时读端可以竞争 } unlock(&rlock);写端: lock(&wlock); // ... 写入 unlock(&wlock);
相关文章:

【linux】线程 (三)
13. 常见锁概念 (一)了解死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程占有的,且不释放的资源,而处于的一种永久等待状态 (二)死锁四个必要条件 互斥条件…...
c++日常积累
在 C 中,可以直接将 int 类型的值赋值给 bool 类型。C 会自动进行类型转换,任何非零的 int 值都会被转换为 true,而 0 会被转换为 false。 QDialog 有一个 finished(int) 信号,该信号在对话框关闭时发出,并传递一个整…...

字节流写入文件
一、创建输出流对象表示的文件三种方式 方法一: FileOutputStream fos new FileOutputStream("fos.txt",true);//最简便方法二: FileOutputStream fos new FileOutputStream(new File("fos.txt"));方法三; File f ne…...
Torch模型导入
冻结param的3种方式 for param in net.lstm.parameters():param.requires_grad Truenet.lstm.requires_grad True # 无效net.lstm.state_dict()[weight_ih_l0].requires_gradFalsenet.lstm.weight_ih_l0.requires_grad False# dir(net.lstm) to validate attributes …...

博弈论:博弈类型空间集合;三层博弈拓展式;
目录 博弈论:博弈类型空间集合 θ(Dss-1=1 )就是博弈类型空间集合; 一、博弈的基本要素 二、博弈的主要类型 三、博弈类型空间集合的构建 三层博弈拓展式: 博弈论:博弈类型空间集合 这的博弈类型空间集合:指一方选择的策略,用符号进行表达:SDss-2(θDss-1=1) = …...
数据库表的关联、集合操作
数据库表的关联、集合操作 join、MySQL、Oracle什么left right的老是忘,归根到底还是不熟练,记录下来,以后就不用再搜了。 设表A、表B分别包含员工信息和部门信息。 表A包含员工的ID、姓名和部门ID, 表B包含部门ID和部门名称。 …...

word怎么清除格式,Word一键清除所有格式教程
你是否曾在编辑Word文档时遇到过复制内容时格式混乱的情况?别担心,这只需要清除一下格式就可以了,很多朋友还不知道word怎么清除格式,下面小编就来给大家讲一讲word一键清除所有格式的方法教程,操作非常简单,有需要的…...

ShardingProxy服务端分库分表
目录 一、为什么要有服务端分库分表? 二、ShardingProxy基础使用 1、部署ShardingProxy 2、配置常用分库分表策略 三、ShardingSphere中的分布式事务机制 1、什么是XA事务? 2、实战理解XA事务 3、如何在ShardingProxy中使用另外两种事务管理器&a…...
开源的 FOC(Field-Oriented Control) 项目
开源的 FOC(Field-Oriented Control) 项目通常用于控制无刷直流电机(BLDC)和永磁同步电机(PMSM)。这些项目可以实现高效的电机控制,广泛应用于机器人、无人机、电动车等领域。以下是一些著名的开…...
高等数学 5.5 反常积分的审敛法 Γ函数
文章目录 一、无穷限反常积分的审敛法二、无界函数的反常积分审敛法三、 Γ \Gamma Γ 函数 一、无穷限反常积分的审敛法 定理1 设函数 f ( x ) f(x) f(x) 在区间 [ a , ∞ ) [a, \infty) [a,∞) 上连续,且 f ( x ) ⩾ 0 f(x) \geqslant 0 f(x)⩾0.若函数 F (…...

宝塔安装ffmpeg的方法
宝塔安装ffmpeg的方法 wget http://download.bt.cn/install/ext/ffmpeg.sh && sh ffmpeg.sh安装完后可输入以下命令是否安装成功: ffmpeg -version...

案例分享-优秀蓝色系UI界面赏析
蓝色UI设计界面要提升舒适度,关键在于色彩搭配与对比度。选择柔和的蓝色调作为主色,搭配浅灰或白色作为辅助色,能营造清新、宁静的氛围。同时,确保文字与背景之间有足够的对比度,避免视觉疲劳,提升阅读体验…...

陪诊小程序之uniapp(从入门到精通)
1.uniapp如何使用vue3编写页面 <template><view class"content"><navbar name"navbar组件"></navbar><image class"logo" src"/static/logo.png"></image><view class"text-area"&…...

深度学习(一)基础:神经网络、训练过程与激活函数(1/10)
深度学习基础:神经网络、训练过程与激活函数 引言: 深度学习作为机器学习的一个子领域,近年来在人工智能的发展中扮演了举足轻重的角色。它通过模仿人脑的神经网络结构,使得计算机能够从数据中学习复杂的模式和特征,…...

源代码加密技术的一大新方向!
在当今这个信息爆炸的时代,企业所面临的数据安全挑战日益严峻。传统的文档加密方法已经无法满足日益复杂的安全需求。幸运的是,SDC沙盒加密系统以其革命性的安全理念和先进技术,为企业提供了一个更可靠、更高效的数据保护方案。 传统加密方案…...

SVN——常见问题
基本操作 检出 提交 更新 显示日志 撤销本地修改 撤销已提交内容 恢复到指定版本 添加忽略 修改同一行 修改二进制文件...

JavaCV 图像灰度化处理
🧑 博主简介:历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,…...

基于Multisim三极管B放大系数放大倍数测量电路设计(含仿真和报告)
【全套资料.zip】三极管B放大系数放大倍数测量电路电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.用三个数码管显示B的大小,分别显示个位、十位和百位。 2.显示范围…...
Molmo模型实战
安装pip文件 conda install pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch-cuda=11.8 -c pytorch -c nvidiapip install ...

免费开源的微信开发框架
近年来,随着人工智能技术的快速发展,聊天机器人在各个领域得到了广泛的应用。在社交媒体中,自动回复成为了一个流行的功能,让用户可以方便地与机器人进行互动。gewe框架,一个开源的微信聊天机器人框架,实现…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...