Linux 线程安全

一、线程安全的概念

线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是 一样的、正确的。那么就说这些线程是安全的。

二、如何保证线程安全

1.线程同步

保证同一时刻只有一个线程访问临界资源。线程同步的方法有4种,信号量、互斥锁、读写锁、条件变量。

2.使用线程安全的函数

在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个函数能被多个线程同时调用且不发生竟态条件,则我们程它是线程安全的。

三、线程安全的函数的应用

【例】在两个线程中分别对一个字符串进行分隔的问题

字符串分隔函数:

在这里插入图片描述
第一次调用结束后,第二次调用的时候给第一个参数传空,就可以沿着原来的字符串继续分隔拿到第二段分割好的字符串。

第一次没有使用线程安全函数的情况的代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>

void* fun(void*arg)
{
    char arr[128]="a b c d e f";//定义一个字符串
    //以空格作为字符串buff的分隔符,将分隔好的字符串内容返回给s
    char* s =strtok(arr," ");
    while(s!=NULL)//如果s为空结束循环,s为空说明字符串buff已经被分隔完了
    {
        printf("fun分隔字符串arr得到的内容为:%s\n",s);
        s=strtok(NULL," ");
        sleep(1);
    }
}


int main()
{
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);

    char brr[128]="1 2 3 4 5 6";
    char* p =strtok(brr," ");
    while(p!=NULL)
    {
        printf("main分隔字符串brr得到的内容为:%s\n",p);
        p=strtok(NULL," ");
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

运行结果:

在这里插入图片描述

结果分析:

这样的运行结果并不是我们所期望的结果,我们希望fun函数分隔字符串arr,main函数分隔字符串brr,但是上面的程序中,main函数却去分隔了fun函数中的字符串arr,子线程fun和主线程main之间彼此影响,然而这并不是我们所期望的。

出现上面结果的原因是,strtok()函数的内部有一个指针用来记录把字符串分隔到哪个位置,这个指针大概率是一个静态变量指针,这样做有一个弊端,就是在线程fun和线程main中调用strtok()这个函数的时候访问静态变量是同一快空间,当线程fun中去调用这个strtok()函数去记录当前的字符串arr被分隔到了哪个位置,但是在线程main中也调用了strtok()这个函数去记录当前的字符串brr被分隔到了哪个位置,意味着线程fun和线程main都要去修改同一块空间中的内容。也就是说,fun线程和main线程都在调用strtok()这个函数,在这两个线程中调用strtok()函数的两条路径同时在执行,strtok()函数中的那个记录字符串分隔位置的指针是一个静态变量,我们在程序中看到在线程fun和main中好像各自都给strtok()函数的这个静态变量分配了一块空间,但是其实不是的,因为在物理内存空间上,这两个线程中strtok()函数的这两块静态变量的空间用的是同一块空间。所以,如果在某一个线程中修改了这块空间中静态变量的值,那么对于另外一个线程来说,这个静态变量的值也发生了变化,因此,这两个线程在运行的时候就会彼此影响,导致最终输出的结果并不是我们所期望的。也就是如果要在多线程中去同时strtok()方法,那么是不可以的,因为strtok()这个方法不是线程安全的。strtok()方法压根就不可以在多线程中使用,因为strtok()函数内部有静态变量,在多线程中同时执行得话,每条路径访问到的静态变量就是同一个,每个线程去记录这个静态变量得信息的时候就会冲突,信息就会被覆盖,程序的执行结果就会产生问题。

比如上图的结果就是因为,main线程和fun线程中调用strtok()函数这两条路径同时执行,第一次分隔之后都得到了对应被分隔的字符串,但是第二次分隔的时候,因为传入的参数为NULL,要从上一次结束的位置去继续分隔字符串,由于上一次分隔字符串结束的位置在fun线程中的arr字符串中,所以当main线程执行的时候,它接收到的被分隔好的字符串是从arr中分隔来的,后续分隔的字符串也都是对arr字符串进行分隔。解决这个问题的思路就是,让两个线程自己用自己的空间,分别记录当前字符串被分隔到哪里,就需要用到这个函数的线程安全版本strtok_r()。

有一类库函数之前都不是线程安全的,引用多线程之后都会存在问题,为之实现了一个多线程的版本,称之为线程安全的版本。这样的函数有很多,比如当前的strtok()函数,它的线程安全版本如下:

在这里插入图片描述

第三个参数 saveptr:是一个二级指针,它需要传入一个指针,传入一个指针以后,通过传入得指针来记录当前字符串被分隔到哪里。这样的话每一个线程都会自己定义这样一个指针,来分别记录它们分隔得字符串分隔到哪个位置,这时,两个线程就不共用同一块空间了。

使用线程安全函数strtok_r()的代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>

void* fun(void*arg)
{
    char arr[128]="a b c d e f";//定义一个字符串
    //以空格作为字符串buff的分隔符,将分隔好的字符串内容返回给s
    char* n=NULL;//定义一个空指针,用这个指针就用来记录字符串分隔的位置
    char* s =strtok_r(arr," ",&n);
    
    while(s!=NULL)//如果s为空结束循环,s为空说明字符串buff已经被分隔完了
    {
        printf("fun分隔字符串arr得到的内容为:%s\n",s);
        s=strtok_r(NULL," ",&n);
        sleep(1);
    }
}


int main()
{
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);

    char brr[128]="1 2 3 4 5 6";
    char* m=NULL;
    char* p =strtok_r(brr," ",&m);

    while(p!=NULL)
    {
        printf("main分隔字符串brr得到的内容为:%s\n",p);
        p=strtok_r(NULL," ",&m);
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

运行结果:

在这里插入图片描述
此时,就是fun线程和main线程不再共用一块空间了,它们各自有各自的空间来来记录自己所分隔的字符串分隔到了哪个位置。这样两个线程同时执行的时候彼此之间就不会影响了,程序也不会产生错误的结果。

四、总结

在多线程中使用库函数的时候一定要使用线程安全版本,这样才能保证线程安全。线程安全指的就是,多线程程序无论调度顺序如何,都能保证程序的结果是正确的,就说该程序处于线程安全的状态。主要的方法就是线程同步和线程安全函数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python中使用xml快速创建Caption和URL书签管理器应用程序

导语&#xff1a; 本文介绍如何使用wxPython库创建一个Caption和URL管理器应用程序。该应用程序具有图形用户界面&#xff0c;允许用户输入Caption和URL&#xff0c;并将其保存到XML文件中。此外&#xff0c;还提供了浏览文件夹并选择HTML文件的功能&#xff0c;并可以运行另一…

低代码开发 轻松解决企业数字化能力建设困局

谈及数字化&#xff0c;这是一个几乎所有领域都在使用的概念。当下&#xff0c;数字化正在经历从以企业为中心向产业为中心转移、从追求效能为主的价值诉求向追求业务创新和业务发展的价值诉求转变&#xff0c;不断增加的不确定性也为数字化的发展蒙上了一层阴影。 除了企业自…

基于Spring Boot的机场VIP客户管理系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的机场VIP客户管理系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java s…

数据结构作业——哈夫曼树

/*【基本要求】 &#xff08;1&#xff09; 从文件中读出一篇英文文章&#xff0c;包含字母和空格等字符。 &#xff08;2&#xff09; 统计各个字符出现的频度。 &#xff08;3&#xff09; 根据出现的频度&#xff0c;为每个出现的字符建立一个哈夫曼编码&#xff0c;并输出。…

深入源码分析kubernetes informer机制(二)Reflector

[阅读指南] 这是该系列第二篇 基于kubernetes 1.27 stage版本 为了方便阅读&#xff0c;后续所有代码均省略了错误处理及与关注逻辑无关的部分。 文章目录 Reflector是什么整体结构工作流程list拉取数据缓存resync操作watch监听操作 总结 Reflector是什么 reflector在informer…

爬虫逆向实战(七)--猿人学第十六题

一、数据接口分析 主页地址&#xff1a;猿人学第十六题 1、抓包 通过抓包可以发现数据接口是api/match/16 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以看出m是加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cook…

云聊天项目测试

前言 以下将对云聊天项目编写测试用例以及主要功能的自动化测试。 1. 测试用例的编写 2. 自动化测试 以下进行部分自动化测试用例的执行&#xff0c;检验项目功能是否符合预期。 2.1 登录功能测试 测试代码&#xff1a; 输入非法用户名或密码逻辑相似&#xff0c;不重复描…

安防监控视频汇聚平台EasyCVR视频平台调用iframe地址无法播放的问题解决方案

安防监控视频汇聚平台EasyCVR基于云边端一体化架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;可提供视频监控直播、云端录像、视频云存储、视频集中存储、视频存储磁盘阵列、录像检索与回看、智能告警、平台级联、云台控制、语音对讲、AI算法中台智能分析无缝…

冷冻冷藏自动化立体库|HEGERLS四向穿梭车助力打造冷链智能仓储新力量

随着中国仓储物流整体规模和低温产品消费需求的稳步增长&#xff0c;冷链市场应用潜力不断释放。而在实际运行中&#xff0c;由于冷库容量不足、基础设施落后、管理机制欠缺等原因&#xff0c;经常出现“断链”现象&#xff0c;严重威胁到产品质量和消费者安全。 河北沃克金属…

React Native expo项目修改应用程序名称

https://expo.dev/accounts/xutongbao/projects npm install --global eas-cli && \eas init --id e32cf2c0-da5b-4a65-814a-4958d58f0ca7 eas init --id e32cf2c0-da5b-4a65-814a-4958d58f0ca7 app.config.js: export default {name: 学习,slug: learn-gpt,owner: x…

Comparable和Comparator区别

Comparable和Comparator接口都是实现集合中元素的比较、排序的&#xff0c;众所周知&#xff0c;诸如Integer&#xff0c;double等基本数据类型&#xff0c;java可以对他们进行比较&#xff0c;而对于类的比较&#xff0c;需要人工定义比较用到的字段比较逻辑。总体来讲&#x…

【微服务技术二】Feign、Gateway(路由、过滤器、跨域)的初步认知

微服务技术二 五、Feign远程调用Feign替代RestTemplate自定义Feign配置方式一&#xff1a;配置文件方式&#xff1a;方式二&#xff1a;java代码方式 Feign性能优化Feign的最佳实践* 六、Gateway网关搭建网关服务路由断言工厂Route Predicate Factory路由过滤器 GatewayFilter默…

负载均衡下的 WebShell 连接

目录 负载均衡简介负载均衡的分类网络通信分类 负载均衡下的 WebShell 连接场景描述难点介绍解决方法**Plan A** **关掉其中一台机器**&#xff08;作死&#xff09;**Plan B** **执行前先判断要不要执行****Plan C** 在Web 层做一次 HTTP 流量转发 &#xff08;重点&#xff0…

排名前 6 位的数学编程语言

0 说明 任何对数学感兴趣或计划学习数学的人&#xff0c;都应该至少对编程语言有一定的流利程度。您不仅会更有就业能力&#xff0c;还可以更深入地理解和探索数学。那么你应该学习什么语言呢&#xff1f; 1.python 对于任何正在学习数学的人来说&#xff0c;Python都是一门很棒…

vue所有UI库通用)tree-select 下拉多选(设置 maxTagPlaceholder 隐藏 tag 时显示的内容,支持鼠标悬浮展示更多

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; 1.需求描述 引用的下拉树形结构支持多选&#xff0c;限制选中tag的个数&#xff0c;且超过制定个数&#xff0c;鼠标悬浮展示更多已选中。 2.先看下效果图 3.实现思路 首先根据API文档&#xff0c;先设置maxTagC…

LVS-DR+keepalived实现高可用负载群集

VRRP 通信原理&#xff1a; VRRP就是虚拟路由冗余协议&#xff0c;它的出现就是为了解决静态路由的单点故障。 VRRP是通过一种竞选的一种协议机制&#xff0c;来将路由交给某台VRRP路由。 VRRP用IP多播的方式&#xff08;多播地址224.0.0.18&#xff09;来实现高可用的通信&…

Redis中的有序集合

前言 本文着重介绍Redis中的有序集合的底层实现中的跳表 有序集合 Sorted Set Redis中的Sorted Set 是一个有序的无重复值的集合&#xff0c;他底层是使用压缩列表和跳表实现的&#xff0c;和Java中的HashMap底层数据结构&#xff08;1.8&#xff09;链表红黑树异曲同工之妙…

记一次mysql8 在linux上安装全过程

参照MYSQL官网官方文档安装 1、mysql官网 mysql官网 2、直接进入文档页 找到安装文档 3、找到自己系统对应的安装文档&#xff0c;选合适的安装方式&#xff0c;我这里使用的是YUM方式 a、开始安装之前需要替换yum仓库 具体步骤如下 b、将下载的文件上传至自己的服务器 如下…

什么是原型(prototype)和原型链(prototype chain)?如何继承一个对象的属性和方法?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 原型&#xff08;Prototype&#xff09;和原型链&#xff08;Prototype Chain&#xff09;⭐ 原型&#xff08;Prototype&#xff09;⭐ 原型链&#xff08;Prototype Chain&#xff09;⭐ 继承属性和方法⭐ 写在最后 ⭐ 专栏简介 前端入…

shell脚本之循环语句

循环语句 循环含义 将某代码段重复运行多次&#xff0c;通常有进入循环的条件和退出循环的条件 for循环语句 一般知道循环次数使用for循环 第一类 格式1&#xff1a; for名称 in 取值次数;do;done; 格式2&#xff1a; for 名称 in {取值列表} do done# 打印20次 for i i…