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

news2024/9/21 14:30:48

目录

⛳️推荐

一、线程安全

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.coloradmin.cn/o/2113979.html

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

相关文章

Mysql高级教程

1.安装部署 安装依赖性&#xff1a; [rootmysql-node10 ~]# dnf install cmake gcc-c openssl-devel ncurses-devel.x86_64 libtirpc-devel-1.3.3-8.el7_4.x86_64.rpm rpcgen.x86_64 下载并解压源码包 [rootmysql-node10 ~]# tar zxf mysql-boost-5.7.44.tar.gz [rootmysql-no…

SpringSecurity原理解析(一)

一、SpringSecurity 核心组件 在SpringSecurity中的jar包有4个&#xff0c;作用分别为&#xff1a; spring-security-coreSpringSecurity的核心jar包&#xff0c;认证和授权的核心代码都在这里面spring-security-config如果使用Spring Security XML名称空间进行配置或Spring S…

营业执照贷款揭秘,不只是有证那么简单!

聊到营业执照贷款&#xff0c;不少人误以为手里有证就能秒到账&#xff0c;这其实是个误区。正经说&#xff0c;这是经营性贷款&#xff0c;放款速度可不是“一刀切”。快的话&#xff0c;一天搞定&#xff1b;慢的呢&#xff0c;三五天到半个月不等&#xff0c;全看你的条件和…

materail3 CircularProgressIndicator和LinearProgressIndicator有难看的白块和断点

看看&#xff0c;就是这个垃圾效果&#xff1a; 圆圈的进度条有断点&#xff0c;不连接&#xff1b; 横线进度条&#xff0c;有尾部亮色&#xff0c;进度处又有分割。 它的原出处在这里&#xff1a;https://m3.material.io/components/progress-indicators/overview&#xff0…

CSP-J基础之cmath常见函数

文章目录 前言1. **sin 函数**2. **cos 函数**3. **exp 函数**4. **log 函数**5. **fabs 函数**6. **pow 函数**7. **sqrt 函数**8. **ceil 函数**9. **floor 函数** 总结 前言 在计算机科学与编程中&#xff0c;数学函数是解决各种计算问题的基础工具。C标准库中的 cmath 头文…

【Qt】处理键盘事件

处理键盘事件 要想获取到用户的键盘按键&#xff0c;在之前的学习中使用过QShortCut&#xff0c;这个函数是信号槽机制封装过获取键盘按键的方式&#xff0c;站在更底层的角度&#xff0c;也可以通过事件获取到当前用户键盘按下的情况。 Qt 中的按键事件是通过 QKeyEvent 类来实…

【Nacos】负载均衡

生产环境相对是比较恶劣的,我们需要对服务的流量进行更加精细的控制.Nacos支持多种负载均衡策略,包括权重,同机房,同地域,同环境等. 1. 服务下线 当某一个节点上接口的性能较差时,我们可以第一时间对该节点进行下线. 操作步骤: 服务详情 ->下线 当点击下线后&#xff0c;…

HarmonyOS】ArkTS学习之基于TextTimer的简易计时器的elapsedTime最小时间单位问题

本文旨在纪录自己对TextTimer使用过程的疑惑问题 我在查看教程时候&#xff0c;发现很多博客在onTimer(event: (utc: number, elapsedTime: number) > void) 这里提到elapsedTime&#xff1a;计时器经过的时间&#xff0c;单位为毫秒。我不清楚是否为版本问题。 在我查看ver…

Linux 进程创建

进程串 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> int main(){int p1,p2,p3,p4;while((p1fork())-1);if(p10){printf("child %d parent %d\n",getpid(),getppid());while((p2fork())-1);if(p20)…

[C++#33][异常] 错误码 | 抛出与捕获 | 异常安全 | 异常体系

目录 C语言与C错误处理方式的对比及应用 一、C语言传统的错误处理方式 1. 终止程序&#xff1a;assert 2. 返回错误码 缺点&#xff1a; 二、C中的异常处理机制 1. 基本概念 2. 异常的抛出与捕获 3. 异常的重新抛出 三、C中的异常安全 1. 构造函数与析构函数的异常 …

数字图像噪声常用的概率分布

高斯、瑞利、指数、埃尔朗分布都是指数家族分布。 注&#xff1a;冈萨雷斯的四版都是错的。 禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》 禹晶、肖创柏、廖庆敏《数字图像处理》资源二维码

KDD 2024 时空数据(Spatio-temporal) ADS论文总结

2024 KDD&#xff08; ACM SIGKDD Conference on Knowledge Discovery and Data Mining, 知识发现和数据挖掘会议&#xff09;在2024年8月25日-29日在西班牙巴塞罗那举行。 本文总结了KDD2024有关时空数据(Spatial-temporal) 的相关论文&#xff0c;如有疏漏&#xff0c;欢迎大…

基于深度学习的遥感图像分类识别系统,使用PyTorch框架实现

取5个场景 [海滩, 灌木丛, 沙漠, 森林, 草地] 划分数据集 train&#xff1a;val&#xff1a;test 7&#xff1a;2&#xff1a;1 环境依赖 pytorch1.1 or 1.0 tensorboard1.8 tensorboardX pillow 注意调低batch_size参数特别是像我这样的渣渣显卡 使用方法 只需要指…

MCU4.逻辑门电路的符号

1.与运算 C语言符号:&(按位与)和&&(逻辑与) 逻辑门电路的符号: 2.或运算 符号:|(按位或)和||(逻辑或) 逻辑门电路的符号: 3.非运算 C语言符号:!(按位非) 逻辑门电路的符号: 4.同或运算 相同为真(0⊙01,1⊙11),否则为假(0⊙10,1⊙00) 符号:⊙(按位同或) 图…

网络学习-eNSP配置ACL

AR1路由器配置 <Huawei>system-view Enter system view, return user view with CtrlZ. [Huawei]undo info-center enable Info: Information center is disabled. [Huawei]interface gigabitethernet 0/0/0 [Huawei-GigabitEthernet0/0/0]ip address 192.168.2.254 24 …

头脑风暴必备:四款在线思维导图工具详解

在快节奏的现代生活中&#xff0c;工作和学习常常需要我们去挖掘新的思维与灵感&#xff1b;在这个过程中&#xff0c;在线思维导图工具无疑是我们的重要伙伴&#xff1b;今天&#xff0c;我们将详细介绍四款在工作和学习中常用的在线思维导图工具给大家&#xff01;&#xff0…

网络安全(sql注入)

这里写目录标题 一. information_schema.tables 和 information_schema.schemata是information_schema数据库中的两张表1. information_schema.schemata2. information_schema.tables 二. 判断注入类型1. 判断数字型还是字符型注入2. 判断注入闭合是""还是 三. 判断表…

数据结构(邓俊辉)学习笔记】排序 5——选取:通用算法

文章目录 1. 尝试2. quickSelect3.linearSelect&#xff1a;算法4. linearSelect&#xff1a;性能分析5. linearSelect&#xff1a;性能分析B6. linearSelect&#xff1a;性能分析C 1. 尝试 在讨论过众数以及特殊情况下中位数的计算方法以后&#xff0c;接下来针对一般性的选取…

「大数据分析」图形可视化,如何选择大数据可视化图形?

​图形可视化技术&#xff0c;在大数据分析中&#xff0c;是一个非常重要的关键部分。我们前期通过数据获取&#xff0c;数据处理&#xff0c;数据分析&#xff0c;得出结果&#xff0c;这些过程都是比较抽象的。如果是非数据分析专业人员&#xff0c;很难清楚我们这些工作&…

【网络安全】服务基础第二阶段——第三节:Linux系统管理基础----Linux用户与组管理

目录 一、用户与组管理命令 1.1 用户分类与UID范围 1.2 用户管理命令 1.2.1 useradd 1.2.2 groupadd 1.2.3 usermod 1.2.4 userdel 1.3 组管理命令 1.3.1 groupdel 1.3.2 查看密码文件 /etc/shadow 1.3.4 passwd 1.4 Linux密码暴力破解 二、权限管理 2.1 文件与目…