Linux学习之路 -- 线程 -- 死锁及线程安全相关问题

news2024/11/26 18:51:58

在上文中,我们已经介绍了线程池的编写,下面补充一下线程的相关知识。

目录

1、线程安全与可重入

<1>概念

<2>区别联系

<3>常见线程不安全的情况

<4>常见的不可重入情况

2、死锁问题

<1>死锁概念

<2>死锁四个必要条件

<3>解决办法

<4>检测与避免死锁

3、其他类型锁的介绍

<1>悲观锁

<2>乐观锁

 <3>自旋锁

<1>引例

<2>概念及使用

4、读者写者问题


1、线程安全与可重入

<1>概念

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

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

<2>区别联系

线程安全是线程在执行中的相互关系,而可重入是函数的一种特点。引起线程安全问题的方法有很多,不可重入是其中的一种。只要是不可重入的函数,一般就是线程安全的。

<3>常见线程不安全的情况

1、不保护共享变量的函数
2、函数状态随着被调用,状态发生变化的函数
3、返回指向静态变量指针的函数
4、调用线程不安全函数的函数

<4>常见的不可重入情况

1、调用了malloc/free函数,因为maloc函数是用全局链表来管理堆的
2、调用了标准I/0库函数,标准I/0库的很多实现都以不可重入的方式使用全局数据结构
3、可重入函数体内使用了静态的数据结构

2、死锁问题

<1>死锁概念

死锁其实就是当多线程在执行同一段代码时,对锁的不合理使用导致每个线程都无法获取锁,从而使得线程处于一种永久阻塞的状态。(如下图)

<2>死锁四个必要条件

1、互斥条件:一个资源每次只能被一个执行流使用
        这个条件简单来说就是要死锁,首先一定要有锁。
2、请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
        这个条件简单来说,就是一个线程持有了一个锁,还想要另一个锁,同时自己又不愿意放弃自己持有的锁。
3、不剥夺条件:一个执行流已获得的资源,在未使用完之前,不能强行剥夺
        这个条件其实就是当一个线程持有锁时,没有用完前,不能强行剥夺锁。
4、循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
        这个条件会稍微抽象一点,不过也好理解。就是线程A持有锁1,线程B持有锁2,在此基础上线程A等待锁2,线程B等待锁1.

<3>解决办法

既然我们已经知道了死锁产生的四个必要条件,那么要避免死锁的产生,只要破坏其中一个条件即可。
        1、互斥条件:要破坏该条件其实就是尽量不使用锁,这样肯定就不会有死锁问题。
        2、请求与保持条件:要破坏该条件,可以让一个线程持有一把锁的情况下(线程需要两把锁),如果继续申请锁失败就直接释放原来的锁,重新取竞争两把锁,也就是放弃保持条件。
        3、不剥夺条件:该条件其实不建议破坏,因为一旦破坏可能造成许多问题。如果实在想要破坏,就在申请锁失败后,强行释放掉该锁
        4、循环等待条件:该条件的破坏其实是最容易的,我们只需要让不同线程在竞争锁时同步即可,也就是多个线程在一定时间段内只竞争同一把锁。

<4>检测与避免死锁

在linux中我们可以使用pstack命令对线程进行查看,该命令显示每个线程的栈跟踪信息(函数调用过程)。该命令的使用方式也很简单,只要pstack + pid即可。在检测死锁问题时,我们可以多次调用pstack命令,多次对比,看看有哪些线程是在多次等锁,而且一直没有什么变换,那么这大概率就是死锁问题导致的。

其中还有避免死锁的检测算法,比较著名的就是银行家算法,该算法在许多博客上都有介绍,这里就不再赘述,当然也还有其他的死锁避免的办法,这里就不详细地介绍了。

3、其他类型锁的介绍

<1>悲观锁

概念: 悲观锁假设在数据被访问的时候,极有可能有其他事务同时修改数据,因此在整个数据处理过程中,将数据锁定,直到事务完成。

特点:

  • 保守:它总是假设最坏的情况,认为每次访问数据时都会产生冲突。
  • 阻塞:如果一个事务持有了锁,其他尝试获取锁的事务将会被阻塞,直到锁被释放。
  • 重试:被阻塞的事务在锁释放后需要重新尝试获取锁。

<2>乐观锁

概念: 乐观锁假设在数据被访问的时候,不会有其他事务同时修改数据,因此在数据修改时并不进行锁定,而是在更新数据的时候检查是否有其他事务同时修改了数据。

特点:

  • 乐观:它假设最好的情况,认为在数据被访问期间不会发生冲突。
  • 非阻塞:事务在读取数据时不会锁定资源,只有在更新数据时才会检查冲突。
  • 冲突检测:在更新数据时,通过版本号、时间戳或者业务逻辑来检测是否有其他事务同时修改了数据。

乐观锁与悲观锁涉及数据库的知识,这里就不详细介绍,简单了解即可。

 <3>自旋锁

在了解自旋锁之前,我们需要先理解自旋的概念,下面用一个例子来帮助理解自旋。

<1>引例

现在有一对好朋友,他们分别叫张三和李四,其中张三是个学渣,而李四是个学霸。到了期末前一天,张三找到李四复习高数。当张三来到李四的宿舍楼楼下,给李四打了个电话,叫李四下来。李四回复说:“我也还在复习,估计还要一两个小时,等我复习完就下来找你”。张三一听李四还要那么久,就决定先到网吧玩一阵子再回来。一个多小时后,李四告诉张三可以过去找他了,此时张三就离开网吧去找李四复习了。

考完高数后,张三又打听到明天要考离散,于是又去找李四复习。当张三来到李四的宿舍楼楼下,给李四打了个电话,叫李四下来。李四回复说:”离散我复习完了,我马上就下来找你“。张三一听李四马上就下来,张三也就并没有去网吧玩了,而是等待李四下来。在李四下来之前,张三反复给李四打电话,确认李四到哪里了。过了一会后,李四下楼后找到了张三,李四和张三一起学习去了。

在上面两个例子中,其实张三就是线程,而李四就是一把锁(资源),网吧就类似于条件变量。在第二个例子中,张三不断打电话向李四确定的过程就是自旋。而在这个过程中造成张三等待方式的差异的原因,其实就是李四(资源)准备就绪的时间。

其实在线程等待锁的过程就是像上述的过程一样,如果时间比较长,线程会被阻塞挂起,等待临界区执行完并释放锁后,再被唤醒去竞争锁。而如果等待的时间比较短,那么线程就不必阻塞挂起,而是一直检测锁是否就绪。

<2>概念及使用

概念:自旋锁是一种让线程在获取锁时不断循环检查锁状态的锁机制,而不是进入休眠状态。

接口:

头文件均为pthread.h 

使用方式和pthread_mutex_t 基本是一致的,区别不大。不过这里需要介绍一下pthread_spin_init接口的pshared参数,这个参数主要用来表示自旋锁是否共享,一般又以下两个选项

  1. PTHREAD_PROCESS_PRIVATE:

    • 当 pshared 设置为 PTHREAD_PROCESS_PRIVATE 时,锁只能被创建它的进程中的线程访问。
    • 这意味着锁是私有的,不能在进程间共享。
    • 这是 pshared 参数的默认值。
  2. PTHREAD_PROCESS_SHARED:

    • 当 pshared 设置为 PTHREAD_PROCESS_SHARED 时,锁可以被多个进程中的线程访问,只要这些进程可以访问到锁所在的内存区域。
    • 这通常意味着锁存储在共享内存区域中。
    • 使用这个值时,需要确保所有访问共享锁的进程都有正确的访问权限。

4、读者写者问题

相较于生产者消费者模型,这个模型在我们日常生活中反而是最常见的,比如我们写文章,发视频等。

这个模型也和生产消费模型一样,具有两个对象,一个交易场所和三种关系。其中两个对象就是读者和写着,三种关系就是写者与读者,写者与写者,读者与读者的关系。写者和写者之间需要保持互斥的关系,读者和写者之间需要保证同步与互斥的关系,读者和读者之间没有关系。因为读者仅仅只是读数据,不对数据进行修改,所以读者之间没有什么关系,这也是和生产消费模型不同的点。

加锁逻辑(伪代码)

相关的接口

上述的上锁逻辑,OS已经帮助我们实现了,我们只需要使用上述的接口,就可以达到上述伪代码的效果。上述各种接口的逻辑和我们之前介绍的互斥锁几乎一致,所以这里就不详细介绍了。需要注意的是,rdlock对应的就是上述伪代码中的读者逻辑,wrlock对应的就是上述伪代码中写者逻辑。

读者优先

如果当前我们读者和写者一起来的时候,并且读者不断到来,读者也没有退完。此时,肯定是让读者先访问共享的资源。

写者优先

读者在写者来临之前可以正常访问资源,一旦写者来了,后续的读者就不能进入了。这个做法比较复杂,不过也是可以通过读写锁实现的,这里不作介绍。

以上就是所有内容,如果想要深入了解文中的内容,可以依据文中介绍的点自行搜索。文中如有不对支持,还望各位大佬指正,谢谢!!!

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

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

相关文章

sql-labs靶场第二关测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、寻找注入点 2、注入数据库 ①Order by判断列数 ②判断回显地方 ③爆库&#xff0c;查看数据库名称 ④爆表&#xff0c;查看security库的所有表 ⑤爆列&#xff0c;查看users表的所有…

Redis-哨兵

概念 Redis Sentinel 相关名词解释 注意: 哨兵机制不负责存储数据,只是对其它的redis-server进程起到监控的作用哨兵节点,也会搞一个集合,防止一个挂了 ⼈⼯恢复主节点故障 用户监控: 实际开发中,对于服务器后端开发,监控程序,是很重要的 服务器长期运行,总会有一些意外,…

16.数据结构与算法-串,数组与广义表(串,BF算法,KMP算法)

串&#xff08;String&#xff09; 串的定义 关于串的术语 串的案例引入 串的类型定义 串的顺序存储结构 串的链式存储结构-块链结构 串的模式匹配算法&#xff08;BF算法与KMP算法&#xff09; BF算法 BF算法时间复杂度 KMP算法

CSS滚动条

通过《CSS盒子模型》一节的学习我们知道&#xff0c;页面中的每个元素都可以看作是一个矩形的盒子&#xff0c;我们可以使用 CSS 来控制盒子的大小、位置等等信息。默认情况下&#xff0c;当元素中的内容超出盒子的大小时&#xff0c;例如元素内容区的宽度和高度所组成的矩形区…

Django连接Dify、ChatGPT4o并计算tokens数量方法

通过Dify可以连接很多模型国内、国外的都可以进行选择可以到Dify里创建一个空白应用&#xff0c;然后点击进入就可以看到API了api_url "http://192.168.15.131/v1/chat-messages" api_key "app-UtzTpVNwpTLUcGvRNnnK9QNY" headers {"Authorization…

网站开发基础:JavaScript

前端开发主要使用的技术如 HTML、CSS 和 JavaScript 等。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>.myDivClass{border: solid red 1px;width: 50px;height:…

python如何查询函数

1、通用的帮助函数help() 使用help()函数来查看函数的帮助信息。 如&#xff1a; import requests help(requests) 会有类似如下输出&#xff1a; 2、查询函数信息 ★查看模块下的所有函数&#xff1a; dir(module_name) #module_name是要查询的函数名 如&#xff1a; i…

【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

PCL 点云统计滤波

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 统计滤波实现 2.1.2 可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&#xf…

Android SystemUI组件(10)禁用/重启锁屏流程分析

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分 应用入口处理流程解读 即可。 在 Android 系统中&#xff0c;禁用锁屏…

C++初学者指南-5.标准库(第二部分)–随机数生成

C初学者指南-5.标准库(第二部分)–随机数生成 文章目录 C初学者指南-5.标准库(第二部分)–随机数生成基本概念例子统一随机数布尔值&#xff08;“抛硬币”&#xff09;正态分布具有独立概率的整数 怎么做种子引擎使用自定义生成器 shuffle算法分布类型概述通用接口均匀分布采样…

虚拟机 VMware 安装 macOS

macOS 界面 MAC OS IOS下载&#xff1a; amacOS Monterey by Techrechard.comwmacOS Monterey by Techrechard.com 下载&#xff1a;Unlocker-v2.0.1-x64 Mac OS X 虚拟机中更改屏幕分辨率 终端输入命令&#xff1a; sudo defaults write /Library/Preferences/com.apple.w…

C++11bind、function、lambda详细讲解

一.lambda表达式 关于lambda表达式&#xff0c;我之前是详细讲过的&#xff0c;现在我们只来做重点讲解&#xff08;如果存在疑问可以回看我之前的作品&#xff09;。 固定格式&#xff1a; []()->返回值{};([capture-list] (parameters) mutable -> return-type { state…

UE行为树编辑器图文笔记

对UE的编辑器实现有点好奇&#xff0c;于是从比较熟悉的行为树编辑器着手分析。以下为阅读UE源码后的个人理解&#xff0c;如有错误请指正。 编辑器基础 扩展编辑器的几种方式 MenuBar 菜单栏ToolBar 工具栏DetailCustomization 自定义细节面板&#xff0c;支持两种方式&…

西安做网站如何打造出色的企业网站

西安做网站如何打造出色的企业网站 随着数字化时代的到来&#xff0c;企业网站已成为展示企业形象、传播品牌价值的重要平台。在西安&#xff0c;如何打造出色的企业网站呢&#xff1f;以下几点建议可以帮助企业在激烈的竞争中脱颖而出。 **1. 清晰的网站定位** 首先&#xff…

【Godot4.3】匀速和匀变速直线运动粒子

概述 本篇论述&#xff0c;如何用加速度在Godot中控制粒子运动。 匀速和匀变速直线运动的统一 以下是匀变速运动的速度和位移公式&#xff1a; v t v 0 a t x t v 0 t 1 2 a t 2 v_tv_0 at \\ x_tv_0t \frac{1}{2}at^2 vt​v0​atxt​v0​t21​at2 当a 0 时&#xf…

计算机科学英语词汇汇总(下)Computer Science English Complete Vocabulary )

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

0基础学习QT——配置开发环境

大纲 安装Qt配置Visual Studio 2022安装插件配置 测试 Qt框架&#xff0c;以其跨平台、高性能以及丰富的UI组件库而著称&#xff0c;是开发图形用户界面应用程序的理想选择。Visual Studio 2022提供了对Qt项目的深度支持&#xff0c;包括智能代码提示、代码导航、调试工具等&am…

(14)MATLAB莱斯(Rician)衰落信道仿真4

文章目录 前言一、改写莱斯分布概率密度函数的理论值二、仿真代码三、仿真结果总结 前言 本文通过将接收信号总功率设置为1&#xff0c;重写了莱斯衰落信道上接收信号幅度的理论PDF式。然后用MATLAB代码生成了在具有不同莱斯因子K的Ricean平坦衰落信道下接收到的信号样本&…

容器适配器-stack、queue、priority_queue和仿函数

目录 1.什么是适配器 2.deque 1.简单了解结构 2.deque的缺陷 3.为什么选择deque作为stack和queue的底层默认容器 3.stack&#xff08;栈&#xff09; 4.queue&#xff08;队列&#xff09; 5.仿函数 6.priority_queue&#xff08;优先级队列&#xff09;&#xff08;堆…