【linux】线程 (三)

news2024/11/26 2:26:18

 

 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);

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

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

相关文章

深度学习(一)基础:神经网络、训练过程与激活函数(1/10)

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

COLA架构生成DDD项目

1.下载代码 https://github.com/alibaba/COLA 2.执行命令 2.1 shell Linux mvn archetype:generate \-DgroupIdcom.xxc \-DartifactIdcola-springboot-demo \-Dversion0.0.1 \-Dpackagecom.xxc.demo \-DarchetypeArtifactIdcola-framework-archetype-web \-DarchetypeGrou…

我是类(最终版)

文章目录 再看构造函数类型转换static静态成员友元内部类匿名对象对象拷贝时的编译器优化 再看构造函数 本标题的目的是解决如下问题&#xff1a;当实现MyQueue时&#xff0c;我们不需要写默认构造函数&#xff0c;因为编译器会调用Stack的默认构造&#xff0c;但是&#xff0…

01 设计模式-创造型模式-工厂模式

工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一&#xff0c;它提供了一种创建对象的方式&#xff0c;使得创建对象的过程与使用对象的过程分离。 工厂模式提供了一种创建对象的方式&#xff0c;而无需指定要创建的具体类。 通过使用工厂模式…

vue 页面导出gif图片 img 导出gif 超简单~

1.首先需要新建一个文件件 新建gif文件夹。这两个文件在文章最后面需要可自提 2.出gif分为两种情况 第一种情况 页面是img标签&#xff0c;直接导出图片作为gif 第二种情况 页面是div标签&#xff0c;需要导出div里面的图片作为gif 2.1页面是img标签&#xff0c;直接导出图…

电脑异常情况总结

文章目录 笔记本无症状息屏黑屏 笔记本无症状息屏黑屏 &#x1f34e; 问题描述&#xff1a; 息屏导致黑屏&#xff1b;依次操作计算机--》右键--》管理--》事件查看器--》Windows日志--》系统&#xff1b;从息屏到异常黑屏之间出现了很多错误&#xff0c;如下&#xff1a;事件…

使用CMake生成动态链接库(.dll和.so)和静态链接库(.lib和.a)的方法

&#xff08;一&#xff09;简介 对于大型软件开发&#xff0c;动态连接库是必不可少的。不仅可以实现模块的封装&#xff0c;而且可以实现软件的热更新&#xff08;即替换windows下的.dll或Linux下的.so文件后直接实现软件更新&#xff0c;无需重新编译&#xff09;。有时也需…

力扣 困难 52.N皇后II

文章目录 题目介绍题解 题目介绍 题解 法一&#xff1a;返回51题N皇后List的长度 法二&#xff1a; class Solution {private int n, ans;private boolean[] onPath, diag1, diag2;public int totalNQueens(int n) {this.n n;onPath new boolean[n];diag1 new boolean[n * …

Standard IO

为了提高可移植性&#xff0c;将通用IO接口经过再封装就形成了标准IO&#xff0c;标准IO不仅适用于Unix环境&#xff0c;也兼容非Unix环境&#xff0c;这也是为什么说我们应该尽可能的使用标准IO&#xff0c;通用IO通过文件描述符fd来与文件交互&#xff0c;为了以示区分&#…

element plus e-table表格中使用多选,当翻页时已选中的数据丢失

摘要&#xff1a; 点击第一页选中两个&#xff0c;再选择第二页&#xff0c;选中&#xff0c;回到第一页&#xff0c;之前选中的要保留&#xff01; element ui table 解决办法&#xff1a; :row-key“getRowKeys” &#xff08;写在el-table中&#xff09; methods中声明 ge…

多一DY4100数字式接地电阻测试仪使用测量方法

接地电阻二线法测量步骤 1、找出一套简易测试线&#xff0c;一根红色两插头线和一根短绿线&#xff1b; 2、找出一根辅助地钉&#xff0c;距离被测接地桩/扁铁约五米远整根打下去&#xff1b; 3、绿线插头接仪器E孔&#xff08;绿色孔&#xff09;&#xff0c;夹子夹需测的接…

ArcGIS002:软件自定义设置

摘要&#xff1a;本文详细介绍安装arcgis10.2后软件自定义设置内容&#xff0c;包括工具条的启用、扩展模块的启用、如何加载项管理器、快捷键设置、样式管理器的使用以及软件常规设置。 一、工具条的启用 依次点击菜单栏【自定义】->【工具条】&#xff0c;根据工作需求勾…

医院信息化与智能化系统(5)

医院信息化与智能化系统(5) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应的…

【漏洞修复/安全补丁】5.4之前版本通过api/products接口进行sql注入的问题

5.4之前版本&#xff0c;会使用api/products接口的selectId参数进行sql注入 修复办法 1、修改crmeb/app/api/controller/v1/store/StoreProductController.php文件中的lst方法里面的接收参数&#xff0c;修改成下图 如果你的版本没有coupon_category_id参数的话&#xff0c;…

No.19 笔记 | WEB安全 - 任意文件操作详解 part 1

1. 任意文件上传漏洞基础 什么是文件上传功能? 在网站和应用中,我们经常会看到允许用户上传文件的功能,比如: 更换头像:让用户上传自己的照片作为头像发布图片:在社交媒体或论坛上传图片提交文档:在办公系统中上传Word、Excel等文档 这些都是常见的文件上传功能。 任意文…

Kettle9.4支持Clickhouse数据源插件开发以及性能测试

前言 最近业务这边有个指标需要用到大数据这边的列式数据库进行处理&#xff0c;由于kettle不支持clickhouse数据源驱动&#xff0c;这里查了一下网上的相关资料&#xff0c;发现了一些别人开发好的驱动包&#xff0c;下载下来后使用效果不尽人意。总结下来有以下几个问题&…

重生之“我打数据结构,真的假的?”--1.顺序表(无习题)

C语言中的顺序表详细总结 1. 概述 顺序表&#xff08;Sequential List&#xff09;是一种线性数据结构&#xff0c;用于存储具有相同数据类型的一组元素。顺序表采用一段连续的存储空间&#xff0c;使用数组来实现&#xff0c;能够高效地支持随机访问操作。在 C 语言中&#…

基于ssm的校园车辆管理系统的设计与实现

文未可获取一份本项目的java源码和数据库参考。 一、选题背景与意义 &#xff08;一&#xff09;选题背景 自改革开放以来&#xff0c;国家开始加大力度对高等校园进行投入&#xff0c;深化了教育体制的改革&#xff0c;全国许多高等校园应运而生&#xff0c;越来越多的孩子走…

C++ —— 《模板进阶详解》,typedef和class的区别以及用法,非类型模板参数,模板的特化,模板的分离编译

目录 1.非类型模板参数 2.模板特化 2.1 概念 2.2 函数模板特化 2.3 类模板特化 2.3.1 全特化 2.3.2 偏特化 3 模板分离编译 3.1 什么是分离编译 3.2 模板的分离编译 4.模板总结 在讲解模板进阶之前&#xff0c;我想先简单单独聊聊class和typename的用法 我们在平时…

Mac使用Git仓库上传文件夹方法

环境&#xff1a;mac 仓库&#xff1a;GitLink Q: 首先我们先了解到&#xff0c;远程下载&#xff08;用ssh&#xff09;仓库会默认下载到电脑的哪里呢&#xff1f; A: 如下图&#xff0c;路径为/Users/bb&#xff0c;&#xff08;bb是我的用户名&#xff09; 那么我们在将a文…