【Linux修行路】线程安全和死锁

目录

⛳️推荐

一、线程安全

1.1 常见的线程不安全情况

1.2 常见的线程安全情况

1.3 常见的不可重入情况

1.4 常见可重入的情况

1.5 可重入与线程安全的联系

1.6 可重入与线程安全的区别

二、死锁

2.1 死锁的四个必要条件

2.2 如何避免产生死锁?


⛳️推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

一、线程安全

  • 线程安全:多个线程并发访问同一段代码时,不会出现问题,就叫做线程安全。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会发生线程安全问题。

  • 重入:同一个函数被不同的执行流调用,当前执行流还没有执行完,就有其它的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。我们所使用的大部分函数都是不可重入的。

只要一个函数是不可重入的,那么在多线程调用的时候可能会引发线程安全问题。

1.1 常见的线程不安全情况

  • 不保护共享变量的函数

  • 函数状态随着被调用,状态发生变化的函数

  • 返回指向静态变量指针的函数

  • 调用线程不安全函数的函数

1.2 常见的线程安全情况

  • 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的

  • 类或者接口对于线程来说都是原子操作

  • 多个线程之间的切换不会导致该接口的执行结果存在二义性

1.3 常见的不可重入情况

  • 调用了 malloc/new 函数,因为 mallco 函数里面是用全局链表来进行管理的

  • 调用了标准 I/O 库函数,标准 I/O 库函数的很多实现都以不可重入的方式使用全局数据结构

  • 函数体内使用了静态的数据结构

1.4 常见可重入的情况

  • 不使用全局变量或静态变量

  • 不使用 malloc 或者 new 开辟空间

  • 不掉用不可重入函数

  • 不返回静态或全局数据,所有数据都由函数的调用者来提供

  • 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

1.5 可重入与线程安全的联系

  • 函数是可重入的,那就是线程安全的

  • 函数是不可重入的,那在多线程的场景下,有可能会引发线程安全问题

  • 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

1.6 可重入与线程安全的区别

  • 可重入函数是线程安全函数的一种

  • 线程安全不一定是可重入的,而可重入函数则一定是线程安全的

  • 如果将对临界资源的访问加上锁,则这个函数就是线程安全的,但是如果忘记释放锁会导致死锁问题,该函数也是不可重入函数。

二、死锁

在使用锁的过程中,导致多线程代码不往后执行了,这就叫做死锁。一般导致死锁的原因是:各个线程均占有不会释放的资源,然后线程相互去申请被其它线程所占用的资源而处于永久等待的状态。这是产生死锁最普遍的情况。当然,还有其它情况,比如一个线程已经申请到了锁,在解锁之前又去申请锁,此时也会导致死锁。

一个线程连续申请锁导致的死锁问题:

void *GrabTickets(void *args)
{
    ThreaInfo *ti = static_cast<ThreaInfo*>(args);
    string name(ti->threadname_);
    while(true)
    {
        pthread_mutex_lock(&lock);
        pthread_mutex_lock(&lock);
        if(tickets > 0)
        {
            usleep(10000);
            printf("%s get a ticket: %d\n", name.c_str(), tickets);
            tickets--;
            pthread_mutex_unlock(&lock);
        }
        else
        {
            pthread_mutex_unlock(&lock);
            break;
        }
        usleep(13); // 用休眠来模拟抢到票的后续动作
        // pthread_mutex_unlock(ti->lock_); // 不能在这里解锁,因为 tickets == 0 的时候就直接跳出循环了,导致锁没有被释放,其它线程就会阻塞住
    }

    printf("%s quit...\n", name.c_str());
}

image-20240314112357270

产生死锁的原因是,当第一个线程来的时候,第一次调用 pthread_mutex_lock(&lock) 成功申请到锁,此时内存空间中的1(锁)被交换到了第一个线程的上下文中,紧接着,第一个线程再次去调用 pthread_mutex_lock(&lock) 申请锁,在 3.3 小节展示的汇编代码中,申请锁的第一步是先把寄存器的值设置为0,而此时第一个线程这个寄存器里面放的是交换进来的1,设置成0以后,就导致 CPU 寄存器中、内存中,都没有1了,锁就这样凭空消失了。所以第一个线程在第二次去申请锁的时候就被挂起了,其它线程在第一次申请锁的时候就会被挂起,最终所有调用该函数的线程都会被挂起,这就是死锁。

一个线程申请到锁后,没有释放也会造成死锁

void *GrabTickets(void *args)
{
    ThreaInfo *ti = static_cast<ThreaInfo*>(args);
    string name(ti->threadname_);
    while(true)
    {
        pthread_mutex_lock(&lock);
        pthread_mutex_lock(&lock);
        if(tickets > 0)
        {
            usleep(10000);
            printf("%s get a ticket: %d\n", name.c_str(), tickets);
            tickets--;
            // pthread_mutex_unlock(&lock);
        }
        else
        {
            // pthread_mutex_unlock(&lock);
            break;
        }
        usleep(13); // 用休眠来模拟抢到票的后续动作
        // pthread_mutex_unlock(ti->lock_); // 不能在这里解锁,因为 tickets == 0 的时候就直接跳出循环了,导致锁没有被释放,其它线程就会阻塞住
    }

    printf("%s quit...\n", name.c_str());
}

image-20240314130236689

2.1 死锁的四个必要条件

所谓必要条件就是,当发生死锁时,下面四个条件都得满足,只要其中有任何一个条件不满足,就不会构成死锁。

  • 互斥条件(前提):一个资源每次只能被一个执行流使用。

  • 请求与保持条件(原则):一个执行流因请求资源而阻塞时,对已获得的资源保持不放。

  • 不剥夺条件(原则):一个执行流已获得的资源,在未使用完之前,不能强行剥夺。

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

2.2 如何避免产生死锁?

理念:破坏上面的四个必要条件,只需要一个不满足即可。

方法:第一个条件可以通过不使用锁来破坏;第二个条件可以通过使用非阻塞接口来申请锁资源进行破坏;第三个条件可以通过释放对应的锁来破坏;第四个条件需要通过程序员编码进行解决。

  • 破坏死锁的四个必要条件

  • 加锁顺序一致

  • 避免锁未释放的场景

  • 资源一次性分配

避免死锁的算法:

  • 死锁检测算法
  • 银行家算法

🎁结语:

        今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!

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

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

相关文章

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台&#xff0c;是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力&#xff0c;在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系…

深入探索 Ubuntu:从基础到高级应用

本文深入探讨了 Ubuntu 操作系统&#xff0c;涵盖了其起源与发展、安装与配置、软件管理、系统优化、网络配置、安全防护以及在不同领域的应用等多个方面。 在起源与发展部分&#xff0c;介绍了 Ubuntu 于 2004 年创立的背景以及其版本的演进。安装与配置环节详细阐述了系统安…

Linux——分离部署,分化压力

PQS/TPS 每秒请求数/ 每秒事务数 // 流量衡量参数 可以根据预估QPS 和 服务器的支持的最高QPS 对照计算 就可以得出 需要上架的服务器的最小数量 PV 页面浏览数 UV 独立用户访问量 // 对于网站的总体访问量 response time 响应时间 // 每个请求的响应时间…

研1日记9

1.理解conv1d和conv2d a. 1和2处理的数据不同&#xff0c;1维数据和图像 b. 例如x输入形状为(32,19,512)时&#xff0c;卷积公式是针对512的&#xff0c;而19应该变换为参数中指定的输出通道。 2.“SE块”&#xff08;Squeeze-and-Excitation Block&#xff09;它可以帮助模…

JAVA:对称加密技术的详细指南

请关注微信公众号&#xff1a;拾荒的小海螺 博客地址&#xff1a;http://lsk-ww.cn/ 1、简述 对称加密是一种加密算法&#xff0c;其中加密和解密使用相同的密钥。其主要特点是速度快、效率高&#xff0c;适用于大数据量的加密需求。对称加密算法通常用于保护数据的机密性和完…

Scratch中秋节游戏——玉兔收集月饼

小虎鲸Scratch资源站-免费Scratch作品源码,素材,教程分享平台! 中秋节将至&#xff0c;想要在这个团圆的日子里增添一点趣味和创意吗&#xff1f;小虎鲸Scratch资源站为大家带来了一款独具特色的中秋节游戏——玉兔收集月饼&#xff01;这款Scratch游戏不仅充满了节日的气氛&am…

小琳AI课堂:MASS模型——革新自然语言处理的预训练技术

大家好&#xff0c;这里是小琳AI课堂。今天我们来聊聊一个在自然语言处理&#xff08;NLP&#xff09;领域非常热门的话题——MASS模型&#xff0c;全称是Masked Sequence to Sequence Pre-training for Language Generation。这是华为诺亚方舟实验室在2019年提出的一种创新模型…

【重学 MySQL】十八、逻辑运算符的使用

【重学 MySQL】十八、逻辑运算符的使用 AND运算符OR运算符NOT运算符异或运算符使用 XOR 关键字使用 BIT_XOR() 函数注意事项 注意事项 在MySQL中&#xff0c;逻辑运算符是构建复杂查询语句的重要工具&#xff0c;它们用于处理布尔类型的数据&#xff0c;进行逻辑判断和组合条件…

MySQL之库和表操作

目录 一&#xff1a;对库的操作 1.创建数据库 2.查看数据库列表 3.显示创建数据库的语句 4.删除数据库 5.字符集与校验集 6.确认当前所处的数据库 7.修改数据库 8.备份和恢复 9.查看连接情况 二:对表的操作 1.创建表 2.查看表 3.删除表 4.修改表 接下来的日…

终端文件管理神器 !!!【送源码】

项目简介 nnn是一款专为命令行爱好者打造的高效终端文件管理器。它以其超小的体积、几乎零配置的要求以及卓越的速度表现而著称。nnn不仅适用于Linux、macOS、BSD等操作系统&#xff0c;还能够在诸如树莓派、Android上的Termux、WSL、Cygwin等多个平台运行。它遵循POSIX标准&am…

Linux——解压大型zip文件报错:bad zipfile offset (local header sig) 的解决方法

一、现象描述 完整一行报错信息&#xff1a; error: invalid compressed data to inflate file #14: bad zipfile offset (local header sig) 二、解决办法 利用 -F 去修复&#xff1a; zip -F xxx.zip --out large.zip得到&#xff1a; 解压&#xff1a; unzip large.zi…

Python爱心射线(完整代码)

目录 系列目录 写在前面​ 完整代码 下载代码 代码分析 写在后面 系列目录 序号直达链接表白系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3

代码随想录训练营Day2 | 209.长度最小的子数组 | 59.螺旋矩阵II | 58. 区间和

1. 学习滑动窗口 2.学习标准输入输出模式 3.学习文档代码随想录 (programmercarl.com) 数组总结 Leetcode 209.长度最小的子数组 题目描述&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组…

Unity Addressables 使用说明(二)管理 Addressables

组织和管理 Addressables 的主要方式是使用组&#xff08;groups&#xff09;和配置文件&#xff08;profiles&#xff09;。本节概述了如何使用这些工具来有效地管理 Addressables。 【概述】管理 Addressables 在决定如何管理项目中的资源之前&#xff0c;先熟悉资源如何创…

CCF推荐A类会议和期刊总结(计算机网络领域)- 2022

CCF推荐A类会议和期刊总结&#xff08;计算机网络领域&#xff09;- 2022 在中国计算机学会&#xff08;CCF&#xff09;的推荐体系中&#xff0c;A类会议和期刊代表着计算机网络领域的顶尖水平。这些会议和期刊不仅汇集了全球顶尖的研究成果&#xff0c;还引领着该领域的前沿发…

Python操作ES集群API(增删改查等)

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 学习B站博主教程笔记&#xff1a; 最新版适合自学的ElasticStack全套视频&#xff08;Elk零基础入门到精通教程&#xff09;Linux运维必备—Elastic…

【信号】信号的保存

信号的保存 信号其他相关常见概念 实际执行信号的处理动作称为信号递达(Delivery) 信号从产生到递达之间的状态,称为信号未决(Pending)。 进程可以选择阻塞 (Block )某个信号。 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.注意,阻塞和…

[数据集][目标检测]智慧农业草莓叶子病虫害检测数据集VOC+YOLO格式4040张9类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4040 标注数量(xml文件个数)&#xff1a;4040 标注数量(txt文件个数)&#xff1a;4040 标注…

linux 安装redis

1. 更新系统和安装依赖 sudo apt update sudo apt install build-essential tcl2. 下载 Redis 源码(没有opt文件夹&#xff0c;则先创建opt文件夹) cd /opt wget http://download.redis.io/releases/redis-6.2.6.tar.gz3. 解压和编译 Redis 解压下载的文件&#xff0c;并进入…

Error: PostCSS plugin autoprefixer requires PostCSS 8.

引言 uniapp坑之使用vue-cli 拉去官方模板出错 版本&#xff1a; node:v14.15.0 npm:6.14.8 Vue CLI v5.0.8 拉取官方模板运行直接报错 原因: 通用说明是&#xff1a; autoprefixer 是版本过高 话说官方咋不解决这个插件问题&#xff0c;那位大佬知道原因 解决&#xff1a;…