当前位置: 首页 > news >正文

【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); //解锁

注意:

  1. 唤醒可以由其它线程去唤醒,如主线程
  2. 三个函数的顺序不要更换(解锁和等待都不是原子操作)

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);

唤醒队列中的所有线程

int pthread_cond_signal(pthread_cond_t *cond);

按顺序唤醒队列中的一个线程

注意:

  1. 唤醒之后的线程需要申请锁资源
  2. 唤醒的时候,可能出现唤醒错误的情况。比如在生产者消费者模型中(多生产者多消费者容易出现这种错误),唤醒了多个生成者,并且消费者此时也是属于唤醒状态,导致生产者和生产者之间,消费者和生产者之间竞争同一把锁,锁最后的分配,可能导致临界资源访问条件不满足(唤醒之后拿到锁资源的线程,有可能这个时候的访问条件不满足,但依然会执行后面的代码),出现错误

正确代码思路

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. 基于环形队列的生产消费模型

(一)了解基于环形队列的生产消费模型

环形队列采用数组模拟,用模运算来模拟环状特性

判断队列是否为空或者为满,我们交给信号量去处理

注意:

  1. 信号量的本质是一个计数器,用来描述临界资源的数目(这里不把临界资源当成一个整体,而是很多份,多个线程并发访问临界资源中的不同份)
  2. 临界资源是否就绪的判断是在临界区之外的(和前一个生产消费模型区分开来),在申请信号量的时候,就已经间接在做判断了(这一步是预定操作,即预定成功,则整个临界资源必有一份资源是属于预定成功的线程的)

(二)基于环形队列的生产消费模型的原理

 

18. 线程池

(一)了解线程池

线程池:

一种线程使用模式

线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程(提前开辟好的),等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度

池化计数:

线程会用到池化技术(以空间换时间),大致操作(C++为例):现在C++类内创建线程(创建的线程当作类内成员变量),再利用原生线程去实现生产者消费者模型

注意:

  1. 在类内创建时,若线程执行的函数也是类内函数,会出现错误(因为执行的函数传参只有一个void*,而类内函数会多一个this指针),所以该执行函数必须是静态的
  2. 若执行函数是静态的,同样会出现一个问题,即静态成员函数无法访问非静态成员变量(没有this指针),所以执行函数的参数void*必须是this指针,通过穿过来的this指针,访问非静态成员变量

(二)线程池的应用场景

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。因为单个任务小,而任务数量巨大, 但对于长时间的任务,线程池的优点就不明显了
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限

(三)线程池示例

  1. 创建固定数量线程池,循环从任务队列中获取任务对象
  2. 获取到任务对象后,执行任务对象中的任务接口

19. 线程安全的单例模式

(一)什么是单例模式

单例模式是一种 "经典的, 常用的, 常考的" 设计模式(这个类的对象,只允许创建一个)

(二)什么是设计模式

针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式

(三)单例模式的特点

  1. 某些类, 只应该具有一个对象(实例), 就称之为单例
  2. 在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百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 默认不是线程安全. 如果需要在多线程环境下使用, 往往需要调用者自行保证线程安全.

  • 智能指针是否是线程安全
  1. 对于 unique_ptr, 由于只是在当前代码块范围内生效, 因此不涉及线程安全问题.
  2. 对于 shared_ptr, 多个对象需要共用一个引用计数变量, 所以会存在线程安全问题. 但是标准库实现的时候考虑到了这个问题, 基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数.

(二)其他常见的各种锁

  1. 悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起
  2. 乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作
  3. CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。
  4. 自旋锁(是否采用自旋锁,取决于临界资源访问的时间的长短,时间短,可以采用自旋锁,即线程不挂起,一直申请所资源

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. 常见锁概念 &#xff08;一&#xff09;了解死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源&#xff0c;但因互相申请被其他进程占有的&#xff0c;且不释放的资源&#xff0c;而处于的一种永久等待状态 &#xff08;二&#xff09;死锁四个必要条件 互斥条件…...

c++日常积累

在 C 中&#xff0c;可以直接将 int 类型的值赋值给 bool 类型。C 会自动进行类型转换&#xff0c;任何非零的 int 值都会被转换为 true&#xff0c;而 0 会被转换为 false。 QDialog 有一个 finished(int) 信号&#xff0c;该信号在对话框关闭时发出&#xff0c;并传递一个整…...

字节流写入文件

一、创建输出流对象表示的文件三种方式 方法一&#xff1a; FileOutputStream fos new FileOutputStream("fos.txt",true);//最简便方法二&#xff1a; FileOutputStream fos new FileOutputStream(new File("fos.txt"));方法三&#xff1b; 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的老是忘&#xff0c;归根到底还是不熟练&#xff0c;记录下来&#xff0c;以后就不用再搜了。 设表A、表B分别包含员工信息和部门信息。 表A包含员工的ID、姓名和部门ID&#xff0c; 表B包含部门ID和部门名称。 …...

word怎么清除格式,Word一键清除所有格式教程

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

ShardingProxy服务端分库分表

目录 一、为什么要有服务端分库分表&#xff1f; 二、ShardingProxy基础使用 1、部署ShardingProxy 2、配置常用分库分表策略 三、ShardingSphere中的分布式事务机制 1、什么是XA事务&#xff1f; 2、实战理解XA事务 3、如何在ShardingProxy中使用另外两种事务管理器&a…...

开源的 FOC(Field-Oriented Control) 项目

开源的 FOC&#xff08;Field-Oriented Control&#xff09; 项目通常用于控制无刷直流电机&#xff08;BLDC&#xff09;和永磁同步电机&#xff08;PMSM&#xff09;。这些项目可以实现高效的电机控制&#xff0c;广泛应用于机器人、无人机、电动车等领域。以下是一些著名的开…...

高等数学 5.5 反常积分的审敛法 Γ函数

文章目录 一、无穷限反常积分的审敛法二、无界函数的反常积分审敛法三、 Γ \Gamma Γ 函数 一、无穷限反常积分的审敛法 定理1 设函数 f ( x ) f(x) f(x) 在区间 [ a , ∞ ) [a, \infty) [a,∞) 上连续&#xff0c;且 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安装完后可输入以下命令是否安装成功&#xff1a; ffmpeg -version...

案例分享-优秀蓝色系UI界面赏析

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

陪诊小程序之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)

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

源代码加密技术的一大新方向!

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

SVN——常见问题

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

JavaCV 图像灰度化处理

&#x1f9d1; 博主简介&#xff1a;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编程&#xff0c;…...

基于Multisim三极管B放大系数放大倍数测量电路设计(含仿真和报告)

【全套资料.zip】三极管B放大系数放大倍数测量电路电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.用三个数码管显示B的大小&#xff0c;分别显示个位、十位和百位。 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 ...

免费开源的微信开发框架

近年来&#xff0c;随着人工智能技术的快速发展&#xff0c;聊天机器人在各个领域得到了广泛的应用。在社交媒体中&#xff0c;自动回复成为了一个流行的功能&#xff0c;让用户可以方便地与机器人进行互动。gewe框架&#xff0c;一个开源的微信聊天机器人框架&#xff0c;实现…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...