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

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

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

相关文章

RK3568平台开发系列讲解(调试篇)debugfs 文件系统

🚀返回专栏总目录 文章目录 一、debugfs使用案例二、enable debugfs三、debugfs API3.1、创建目录3.2、创建文件3.3、帮助函数四、使用示例📢Linux 上有一些典型的问题分析手段,从这些基本的分析方法入手,你可以一步步判断出问题根因。这些分析手段,可以简单地归纳为下图…

Linux·文件与IO

1. 回忆文件操作相关知识 我们首先回忆一下关于文件的一些知识。 如果一个文件没有内容&#xff0c;那它到底有没有再磁盘中存在&#xff1f;答案是存在&#xff0c;因为 文件 内容 属性&#xff0c;即使文件内容为空&#xff0c;但属性信息也是要记录的。就像进程的…

基于STM32的Android控制智能家政机器人

基于STM32的Android控制智能家政机器人 基于STM32的Android控制智能家政机器人一、项目背景与意义二、系统设计方案三、硬件电路设计四、软件设计与实现4.1 Android端软件设计4.2 机器人端软件设计 五、系统调试与测试六、结论与展望七、附录 基于STM32的Android控制智能家政机…

从外行人的角度解释1Bit的模型,是怎样改变世界的

一个框架&#xff0c;和一篇论文&#xff0c;改变了模型训练的规则 框架是BitNET 论文https://arxiv.org/abs/2410.16144 有人问我什么是1.58Bit 是这样的。 fp16是一般情况下模型训练后产物的精度。 比如qwen2 8B fp16&#xff0c;文件大小15GB 如果量化成Q_4O&#xff…

24下河南秋季教资认定保姆级教程

教师资格认定前需要做的准备材料 准备身份证户口本 居住证 学生证 教师考试合格证明 普通话证书 学历证书 体检合格证书 近期一寸白底证件照 网上报名 河南24下教资认定 网上报名时间&#xff1a;10月21日-11月1日 现场确认 网上审核未通过的宝子&#xff0c;需要…

html+css+js实现Notification 通知

实现效果&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Notif…

k8s use-context是什么

kubectl 的 use-context 命令用于在 Kubernetes 集群中切换上下文&#xff08;context&#xff09;&#xff0c;从而方便地在多个集群或命名空间之间进行操作。一个上下文定义了 kubectl 使用的 集群、用户 和 命名空间 的组合。 use-context 的作用&#xff1a; 每个上下文&…

AOP学习

corol调用serverce不在是直接调用的是调用底层代理对象&#xff0c;由代理对象统一帮我们处理 AOP常见概念 通知类型 切面顺序

【C++】— 一篇文章让你认识STL

文章目录 &#x1f335;1.什么是STL&#xff1f;&#x1f335;2.STL的版本&#x1f335;3.STL的六大组件&#x1f335;4.STL的重要性&#x1f335;5. 如何学习STL&#x1f335;6. 学习STL的三种境界 &#x1f335;1.什么是STL&#xff1f; STL是Standard Template Library的简称…

Matlab软件进行金融时间序列数据的描述性统计代码

1、数据S&P500的收盘价格&#xff0c;return100*log(pt/pt-1) 方法1&#xff1a;用python代码 import numpy as np import pandas as pddef calculate_log_returns(prices):"""计算价格序列的对数收益率。参数:prices (numpy.array): 价格序列。返回:log_…

【实战指南】Vue.js 介绍组件数据绑定路由构建高效前端应用

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技巧(编写中……&#xff09; 5、面经吐血整理的 面试技…

ChatGPT 现已登陆 Windows 平台

今天&#xff0c;OpenAI 宣布其人工智能聊天机器人平台 ChatGPT 已开始预览专用 Windows 应用程序。OpenAI 表示&#xff0c;该应用目前仅适用于 ChatGPT Plus、Team、Enterprise 和 Edu 用户&#xff0c;是一个早期版本&#xff0c;将在今年晚些时候推出"完整体验"。…

LeetCode 热题100之哈希

1.两数之和 思路分析1&#xff08;暴力法&#xff09; 双重循环枚举满足num[i] nums[j] target的索引&#xff0c;刚开始不知道如何返回一对索引。后来知道可以直接通过return {i,j}返回索引&#xff1b;注意&#xff1a;j应该从i1处开始&#xff0c;避免使用两次相同的元素…

liunx线程

线程的概念 程序中的一个执行路线就是线程&#xff0c;线程就是一个进程内部的控制序列一个进程至少都有一个执行线程线程在进程内部运行&#xff0c;本质是在进程地址空间内运行liunx系统下&#xff0c;cpu眼里的PCB比传统进程更加轻量化透过虚拟地址空间&#xff0c;把进程的…

展会亮点回顾|HMS汽车工业通信解决方案

2024 汽车测试及质量监控博览会&#xff08;中国&#xff09;&#xff08;Testing Expo China – Automotive&#xff09;于 8 月 28 日至 30 日在上海世博展览馆顺利举行。作为汽车测试技术领域的顶级盛会&#xff0c;来自全球的行业领袖和技术专家齐聚一堂&#xff0c;共同探…

即时通讯 离线消息处理初版

离线消息处理 NotOnlineExecute package com.example.im.infra.executor.send;import com.example.im.endpoint.WebSocketEndpoint; import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springf…

Django学习(三)

Django的设计模式及模板层 传统的MVC&#xff08;例如java&#xff09; Django的MTV 模板层&#xff1a; 模板加载&#xff1a; 代码&#xff1a; views.py def test_html(request):#方案一# from django.template import loader# 1. 使用loader加载模板# t loader.get_…

ICP算法理解入门之RT求解

本文仅仅分析了一下两组点云做好匹配好了之后&#xff0c; 如何解算RT&#xff0c; 并不涉及匹配过程&#xff0c;详细的匹配&#xff0c;下次再出一篇博客 ICP 求解旋转矩阵 ( R ) 的步骤 给定两个点云集合 ( P {p_1, p_2, …, p_n} )&#xff08;源点云&#xff09;和 ( Q…

uniapp-uniapp + vue3 + pinia 搭建uniapp模板

使用技术 ⚡️uni-app, Vue3, Vite, pnpm &#x1f4e6; 组件自动化引入 &#x1f34d; 使用 Pinia 的状态管理 &#x1f3a8; tailwindcss - 高性能且极具灵活性的即时原子化 CSS 引擎 &#x1f603; 各种图标集为你所用 &#x1f525; 使用 新的 <script setup> …

多ip访问多网站

作业要求 配置nginx服务通过ip访问多网站 [rootlocalhost ~]# systemctl stop firewalledFailed to stop firewalled.service: Unit firewalled.service not loaded. [rootlocalhost ~]# mount /dev/sr0 /mnt mount: /mnt: /dev/sr0 已挂载于 /run/media/redhat/RHEL-9-3-0-B…