Linux线程补充1

十、多线程中线程间的"独立"

​ 1.线程在代码段通过执行不同的函数,实现代码段的独立;

​ 2.新线程通过在共享区划分不同的管理属性和不同的栈空间,实现栈的独立,而主线程使用的是栈空间;

​ 3.线程通过获取到不同的堆空间地址,实现堆的独立;

​ 总结:线程是通过对共享资源的划分来实现资源的独立的,但是只要将其他线程的资源地址添加到当前线程当前线程也是可以访问到的;下图main thread访问了thread-2的局部变量;

在这里插入图片描述

const int num = 10;
void *threadRoutine(void *args)
{const char *s = static_cast<const char *>(args);int cnt = 1;while (cnt){cnt++;cout << "thread name: " << s << " pid: " << getpid() << endl;if (cnt == 10)break;}return nullptr;
}
string str;int main()
{vector<pthread_t> vtid;for (int i = 0; i < num; i++){pthread_t tid;str += "thread";str += " : ";str += to_string(i);pthread_create(&tid, nullptr, threadRoutine, (void *)str.c_str());vtid.push_back(tid);}for (int i = 0; i < vtid.size(); i++){pthread_join(vtid[i], nullptr);}return 0;
}

十一、线程局部存储

11.1__thread

​ __thread是编译器提供的一个编译选项,让被修饰变量在线程的局部存储里都开辟一份;,本质上就不是同一个变量了;需要注意的是**__thread只能修饰内置类型**;通常是用来存储线程的属性tid、pid之类的;

__thread int val_;
//定义一个全局变量被__thread修饰,全局变量对于每一个线程都是各自私有一份;
//此技术叫做线程的局部存储;

11.2局部存储与独立栈的区别

​ 独立栈主要是用来保证调用链的安全,里面放的是临时变量;

​ 而局部存储类似全局数据区,存放的数据是随线程的整个生命周期的;

十二、线程分离

​ 类似进程非阻塞等待;

​ 默认创建的新线程是joinable需要被等待的,不进行等待无法释放资源,会造成内存泄露;主线程不关心新线程的退出情况,那是由进程来关心的,主线程关心的是函数执行完之后的返回值;如果主线程不想知道新线程的返回值,就不需要阻塞等待,此时要进行线程分离;

​ 线程分离即新线程自己将内核管理的LWP结构和库管理的tcb结构释放;

注意,1.线程分离之后是不可以被join的;2.必须保证主线程是最后一个退出的,否则会导致整个进程的退出

​ 一个线程是否被分离是需要被记录下来的,所以一个线程被分离本质就是将本线程的tcb里的线程可标志位设置为1;

12.1线程分离接口

#include <pthread.h>
int pthread_detach(pthread_t thread);
//即不需要主线程阻塞等待,执行完之后自己释放;
//可以由主线程进行分离
pthread_t tid;
pthread_detach(tid);
//新线程自己释放自己
pthread_detach(pthread_self());

十三、线程的同步与互斥

​ 多线程并发访问共享资源的时候是有数据不一致问题的;

互斥:任何时候访问资源只有允许有一个执行流;

同步:按照一定的顺序性获取资源;

原子性:在代码上表现为只有一个汇编指令,只有执行前和执行后两种状态;

​ 对一个全局变量进行多线程并发–/++操作是不安全的,因为如–操作需要进行三步操作:1.将内存中的数据读取到CPU当中;2.在CPU内部进行–操作;3.将计算结果写回内存;

​ 即CPU执行是按照汇编走的,应用层的一行代码会被转换成多条汇编指令,所以一行代码不具有原子性;

​ CPU的寄存器保存着线程的上下文,当线程被换走的时候,要从寄存器中拿走上下文,换回是要将上下文恢复到寄存器中,对于–操作最后是要将寄存器中的内容写入到内存当中的,对于共享资源多线程并发访问时,当前线程因为时间片结束导致数据没有被修改并将数据保存到上下文中,其他线程对数据成功进行了修改,当线程恢复上下文是就会用自己的上下文修改数据,但是此时的数据已经不是刚从内存读取到的数据了,这时修改内存数据就会有问题;

​ 总结:多线程并发访问时会使得数据同时被修改,造成数据不一致问题;

​ 解决方式:任何时候共享数据只允许有一个执行流,即互斥,这样某个线程执行的时候,其他线程就不会修改共享数据;使用锁的方式实现互斥;

​ 互斥场景下,1.由于线程对于锁的竞争能力不一样会导致其他线程饥饿问题;2.由于大量线程是被阻塞的,一旦资源就绪,默认所有线程就会都从阻塞态变成运行态竞争锁,但是最多只有一个线程可以访问,其他线程是无效的,被伪唤醒了,而且会导致被唤醒的线程CPU都要执行一次检测逻辑,这就叫做惊群问题;

​ 解决方式:1.所有的线程必须排队;2.释放完锁的线程不能立马申请锁,排到队尾;

​ 这样可以让线程获取锁有一定的顺序,至少保证了每个线程都有机会获得锁,是相对公平的;

十四、互斥锁

​ 锁是一种共享资源是通过保证申请和释放锁的原子性来保证访问锁是安全的;

​ 先定义一把锁,然后初始化和销毁,期间对临界区加锁和解锁;

14.1锁的初始化和释放

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//参数是锁的地址;
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
//第一个参数是锁的地址;第二个参数是锁的属性一般为nullptr;
pthread_mutex_t/*是库里面提供的一种数据类型*/ mutex = PTHREAD_MUTEX_INITIALIZER;
//使用全局或者静态的方式进行初始化之后是不需要destroy的;

14.2加锁和解锁

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);//加锁,申请成功可以访问,失败阻塞;
int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞加锁,解决死锁问题;
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁;底层设计是为了解决死锁问题;

14.3加锁后的缺陷

​ 加锁会导致加锁区域进行串行访问,本质上是以时间换取安全;所以应该尽量保证临界区即加锁的区域的代码要少;好处是执行时间少,串行比率降低,其他线程等待的时间减少;

​ 加锁产生饥饿问题;

14.4加锁的细节

​ 1.申请锁成功才可以继续往后执行;不成功就会阻塞等待资源就绪;

​ 2.加锁之后必须解锁,否则会导致锁资源不就绪引发其他线程一直阻塞的问题,所以要保证锁一定可以被解锁,保证不会因为进行跳转不能执行到解锁;

​ 3.不同线程对锁的竞争能力不同;不同线程对锁的访问是并发的;为了保证竞争公平要进行同步;

​ 4.如果线程长时间得不到锁就会导致饥饿问题,即纯互斥环境,锁资源分配不够合理,导致其他进程的饥饿问题;

​ 5.加锁后的线程也是可以被切换的,但是此线程还是持有锁,没有释放锁,其他线程是无法访问临界区的;因为其他线程只关心当前线程是持有锁还是释放锁,即是此线程对于其他线程是原子的;

14.5互斥锁的底层实现

​ 大部分芯片在设计时一定要内置汇编指令集才能执行各种复杂的逻辑;

​ 互斥锁底层使用了swap这样的指令,作用是将内存和寄存器中的值进行交换,一条指令所以是原子的,即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期,因为多处理器也只有一套总线和内存连接,CPU访问内存是要经过硬件仲裁器决定有哪一个CPU去访问内存;即多处理器CPU访存还是串行的,只是计算是并发的;

​ 伪代码:

​ 每一个线程的上下文都是私有的,即寄存器中的数据都是属于线程的;线程的结构中上下文是软件上下文,在寄存器中是硬件上下文;锁本质上就是一个数字1,只有一个,swap就可以保证锁申请成功,其他指令都是后续动作

​ 交换的本质就是将内存中的数据(共享数据)交换到寄存器中,也就是线程的硬件上下文(私有数据)中;如果当前线程继续是申请锁就会导致唯一的1也变成0,即没人能够申请锁了,死锁了;

lock://8086的ax寄存器是16位的,后来eax是32位,e是expand扩展的意思,分为了ah和al各16位movb $0,%al//将寄存器al的值设为0xchgb al, mutex//将寄存器的值交换到变量mutex里,原本mutex是1变为了0,这就是申请锁的过程if(al寄存器的内容 > 0)//申请成功此时寄存器内部就是1,否则就是失败挂起等待{return 0;)else挂起等待;goto lock;
unlock:movb $1, mutex//将内存中的mutex变量置为1,这样的实现就可以使得其他线程来释放锁,而不是必须当前线程来释放锁;这样就可以实现两个线程的同步;如果使用的是swap就必须保证是申请锁成功的线程来释放锁;为解决死锁问题提供了方案;唤醒等待Mutex的线程;return 0;

14.6锁的应用——封装

#include <pthread.h>class mutex
{
public:mutex(pthread_mutex_t *lock) : lock_(lock) {}void lock(){pthread_mutex_lock(lock_);}void unlock(){pthread_mutex_unlock(lock_);}~mutex() {}private:pthread_mutex_t *lock_;
};
class lockguard
{
public:lockguard(pthread_mutex_t *lock) : mutex_(lock){mutex_.lock();}~lockguard(){mutex_.unlock();}private:mutex mutex_;
};
//可以使用代码块{}来划分对象的的生命周期

14.7死锁

​ 同一把锁申请两次就会形成死锁,原因是底层锁其实就是1,申请了两次使得整个过程中1丢失了,所以死锁了;

14.7.1概念

​ 死锁是指在一组执行流中的各个执行流均占有不会释放的资源,但因互相申请被其他执行流所占用不会释放的资源而处于的一种永久等待状态。

14.7.2死锁四个必要条件

​ 互斥条件:一个资源每次只能被一个执行流使用;

​ 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放; 默认使用的lock接口就是请求与保持的;

​ 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺;

​ 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系;

​ 缺一不可;

14.7.3解决死锁问题

​ a.破坏死锁的必要条件,如:

​ 1.不使用互斥;2.使用pthread_mutex_trylock(pthread_mutex_t *mutex),申请失败返回然后将自己的锁释放;3.由于释放锁的底层是直接赋1,所以可以由其他线程释放当前线程的锁的;4.每一个线程申请锁的顺序要一致,否则容易形成环路申请问题;

​ b.加锁顺序一致;

​ c.避免锁未释放的场景;

​ d.资源一次性分配;

​ 相关算法:1.死锁检测算法;2.银行家算法;

十五、线程安全问题

​ 线程安全:多个线程并发同一段代码时,不会出现不同的结果。

常见的线程不安全的情况:

​ 1.不保护共享变量的函数;

​ 2.函数状态随着被调用,状态发生变化的函数;

​ 3.返回指向静态变量指针的函数;

​ 4.调用线程不安全函数的函数;

​ 重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入

​ 一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则是不可重入函数。

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

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

相关文章

2 Spring之IOC详解

文章目录 4&#xff0c;IOC相关内容4.1 bean基础配置4.1.1 bean基础配置(id与class)4.1.2 bean的name属性步骤1&#xff1a;配置别名步骤2:根据名称容器中获取bean对象步骤3:运行程序 4.1.3 bean作用范围scope配置4.1.3.1 验证IOC容器中对象是否为单例验证思路具体实现 4.1.3.2…

计算平均分 javascript

养成好习惯&#xff1a;先写注释再写代码 基础版&#xff1a;直接写逻辑&#xff08;平均分总和/个数&#xff09; // 求平均分 var scores [60, 55, 80, 33, 75, 100]; // 求和,相除 var sum 0; var avg;for (var i 0; i < 6; i) {sum scores[i]; }avg sum / 6; con…

美团一路稳扎稳打,稳健型选手是如何炼成的?

2023年至今&#xff0c;宏观环境出现多元变化。这种变化带来了一种独特的机遇——当下&#xff0c;可能是观察一家公司或一种行业抗周期能力的最好时期。因为在面对不可预知的未来时&#xff0c;它们的抉择&#xff0c;一方面会反映业务基本盘的可靠性&#xff0c;另一方面则能…

节后必看!复工复产九项安全提示!

本文转载自“中华人民共和国应急管理部”公众号&#xff0c;原文链接&#xff1a;节后必看&#xff01;复工复产九项安全提示&#xff01; 春节假期结束&#xff0c;不少工厂企业开始复工复产&#xff0c;安全防范不可疏忽大意&#xff0c;复工复产时期主要危险因素 01假期综合…

基于SSM+Jsp+Mysql的记账管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

基于springboot+vue的旅游推荐系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【redis】服务器架构演进

架构演进 单机架构应用数据分离架构应⽤服务集群架构读写分离 / 主从分离架构冷热分离架构垂直分库微服务架构 单机架构 所有的应用服务、业务所需的数据、业务处理等都在一台服务器上。 在初期&#xff0c;用户访问量很少&#xff0c;对服务器的的性能和安全没有很高的要求&am…

验证回文串——算法思路

题目链接&#xff1a;125. 验证回文串 - 力扣&#xff08;LeetCode&#xff09; 主要是将大写字母转换成小写&#xff08;将字母异或32即可转换大小&#xff09;&#xff0c;并将数字也存入数组&#xff0c;除去其他字符。反转是通过list反相输入再比较实现的。 public static …

踩了一天Prophet的fbprophet坑

pip怎么安装Prophet 安装了这个不行,要安装fbprophet 然后安装不起 哦豁 anaconda虚拟环境安装好将其导入pycharm from fbprophet import Prophet 然后不报错了,很稀奇对吧,不报错了 但是运行还是给你显示 没有fbprophet 绝望了,找人吧 通过官方网站安装最新版Prophet,但是…

2024年全球生成人工智能全景图【中文】

2024年全球生成人工智能全景图【中文】 在过去的一年中&#xff0c;产生式人工智能&#xff08;GenAI&#xff09;无疑成为了全球各行各业的热门话题。特别是ChatGPT的发布&#xff0c;激发了公众对GenAI强烈的兴趣和激动&#xff0c;唤醒了我们对其变革潜力的认知。 虽然我们…

keepalived高可用

负载均衡集群 底层协议&#xff1a;VRRP协议 优点&#xff1a; 工作原理&#xff1a; 体系结构图 内核空间&#xff1a;IPVS模块 NETLINK模块 用户空间&#xff1a;I/O模型 内存管理方法&#xff1a;Memory mngt 控制面板&#xff08;配置文件解析器&#xff09; 核心组件…

国自然提交状态,NSFC已审核 ≠ 申请书被受理!!!

本 期 推 荐 【SciencePub学术】2024年度国家自然科学基金集中受理期项目申请受理工作已基本结束。到底什么状态才算申请书被NSFC接收成功呢&#xff1f; 01 申请书状态 申请人登录ISIS系统&#xff0c;至此&#xff0c;绝大部分申请人的系统状态为下面三种&#xff1a; …

安科瑞ANET智能物联网网关 通信管理机-安科瑞 蒋静

概述 本系列智能通信管理机是一款采用嵌入式硬件计算机平台&#xff0c;具有多个下行通信接口及一个或者多个上行网络接口&#xff0c;用于将一个目标区域内所有的智能监控/ 保护装置的通信数据整理汇总后&#xff0c;实时上传主站系统&#xff0c;完成遥信、遥测等能源数据采集…

2024公认口碑最好的洗地机有哪些?若看重清洁力,这四款最值得买

每当我们要清洁卫生时&#xff0c;是否总是感到腰酸背痛、疲劳不堪&#xff0c;甚至头昏眼花&#xff1f;地板是家中的重要门面&#xff0c;不容忽视的卫生焦点。如今&#xff0c;我们终于多了一位家务打扫的救星——家用洗地地机。一次操作&#xff0c;即可完成扫地除尘、地除…

【Java开发过程中的流程图】

流程图由一系列的图形符号和箭头组成&#xff0c;每个符号代表一个特定的操作或决策。下面是一些常见的流程图符号及其含义&#xff1a; 开始/结束符号&#xff08;圆形&#xff09;&#xff1a;表示程序的开始和结束点。 过程/操作符号&#xff08;矩形&#xff09;&#xff…

【排序算法】实现快速排序值(霍尔法三指针法挖坑法优化随即选key中位数法小区间法非递归版本)

文章目录 &#x1f4dd;快速排序&#x1f320;霍尔法&#x1f309;三指针法&#x1f320;挖坑法✏️优化快速排序 &#x1f320;随机选key&#x1f309;三位数取中 &#x1f320;小区间选择走插入&#xff0c;可以减少90%左右的递归&#x1f309; 快速排序改非递归版本&#x1…

瑞士百达资产管理有限公司拟增三大去中心化数字加密货币支付接口!

简介: 瑞士百达集团成立于1805年,欧洲第三大财富管理公司, 集团拥有约 5,300 名员工,其中包括 900 名投资经理。它在金融服务中心拥有 30 个办事处网络,包括在日内瓦、卢森堡、拿骚、香港和新加坡的注册银行,百达集团管理的资产总额达6380亿瑞士法郎(7670亿美元)。 瑞士百达资…

一键部署开源舆情系统

系统展示 部署 docker run -itd --name stonedt_yuqing -p 8085:8085 registry.cn-beijing.aliyuncs.com/stonedt_yuqing/stonedt_yuqing:1.0.6 登录 ip:8085 默认用户名&#xff1a;13900000000 &#xff0c;密码&#xff1a;stonedt

C# Solidworks二次开发:获取主窗口API和创建新活动窗口API详解

今天要讲的是Solidworks中的两个API。 &#xff08;1&#xff09;Frame Method (ISldWorks)&#xff1a;获取SOLIDWORKS主框架。 下面是API中给出的例子&#xff1a; public void Main(){ModelDoc2 swModelDoc default(ModelDoc2);Frame swFrame default(Frame);ModelWindow…

蓝桥杯算法心得——附近最小(优先队列+滑动窗口)

大家好&#xff0c;我是晴天学长&#xff0c;这题可以用贪心优先队列和滑动窗口来写&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .附近最小 问题描述 小蓝有—个序列a[1], a[2],...,a[n]。 给定—个…