线程安全--互斥锁

news2024/9/25 20:44:28

在这里插入图片描述

文章目录

  • 一.线程安全问题
      • 读取无效(脏)数据
      • 丢失更新
      • 线程安全的保证--操作的原子性
  • 二.互斥锁及其实现原理
    • 互斥锁的实现原理
    • pthread线程库提供的锁操作
  • 三.死锁问题

在这里插入图片描述

一.线程安全问题

  • 当多个线程并发地对同一个共享资源进行修改操作时,可能会引发数据读写错误(比如读取无效(脏)数据,丢失更新等等)

读取无效(脏)数据

  • 多个线程修改同一个全局变量的示例(模拟抢票):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.cpp"

using namespace std;

//四个线程模拟抢票
#define NUM 4
//用于记录线程信息的类
class threadData
{
public:
    threadData(int number){
        threadname = "thread-" + to_string(number);
    }
public:
    string threadname;
};

//10张票作为临界资源
int Tickets = 10;

//线程执行流
void * GetTickets(void * args){
    //获取线程名
    threadData * TName = static_cast<threadData *>(args);
    //执行抢票逻辑
    while(true){
        if(Tickets > 0){
            usleep(10000);
            Tickets--;//修改临界资源
            cout << TName->threadname << "Get one ticket, tickets left:" << Tickets << endl; 
        }
        else{
            break;
        }
        usleep(10000);
    }
    cout << TName->threadname << "exit" << endl;
    return nullptr;
}

int main(){
    //线程名数组
    vector<threadData*> threadName(NUM);
    //线程标识符数组
    vector<pthread_t> threads(NUM);
    //创建4个线程
    for(int i = 0; i < NUM; ++i){
        threadName[i] = new threadData(i+1);
        pthread_create(&threads[i],nullptr,GetTickets,threadName[i]);
    }


    //轮询阻塞线程等待
    for(int i =0 ; i < NUM ; ++i){
        pthread_join(threads[i],nullptr);
    }

    //线程名结构体释放
    for (auto td : threadName)
    {
        delete td;
    }
    return 0;
}

在这里插入图片描述

  • 代码逻辑限制共享变量Tickets不能小于零,但实际执行结果显示共享变量Tickets多线程环境中被减到了-1,引发该错误的原因如下图所示:
    在这里插入图片描述
  • 线程在if(Tickets > 0)处读取到了无效的数据

丢失更新

  • C/C++中对共享变量的++,--操作也是非线程安全的,Var++的汇编代码:
    在这里插入图片描述
    在这里插入图片描述

  • 两个线程并发对共享变量int Var = 10进行++操作引发的丢失更新问题

时间线程1线程2Var的值
1Mov [Var] ,%eax (CPU调度切换至线程2)10
2Mov [Var] ,%eax (CPU调度切换至线程1)10
3Inc %eax10
4Mov %eax,[Var]11
5Inc %eax11
6Mov %eax,[Var]11
  • 两次++并发操作只有一次有效

线程安全的保证–操作的原子性

  • 在多线程环境中,要确保线程安全,各个线程对于同一共享资源的修改操作必须是串行执行的(或者执行过程是可串行化的),即同一时刻只能有一个线程同一共享资源进行修改操作.
    • 满足这样性质的操作称为原子性操作,原子性操作:不可拆分的最小执行单位(一次操作在某个线程中执行完毕之前不可被其他线程重入)
    • 在计算机系统中,最基本的原子性操作就是一条汇编语句,一条汇编语句的执行是不会因为CPU执行流调度切换而中断的,因而是线程安全的,其他操作的原子性只能通过互斥锁来保证

二.互斥锁及其实现原理

互斥锁的实现原理

  • 锁的本质是进程中的共享资源,可以理解为内存中的一个0/1标记位,对于进程而言锁是一个全局变量

  • 线程加锁的本质是将内存中的的锁变量(值为1)交换到CPU中的某个特定的寄存器中(寄存器的初始值为0),当线程被切换时,会将它在CPU中的执行流上下文信息(包括锁标记1)保存到PCB中,相当于线程"带着锁一起被切换掉了"

  • 因此线程持有锁的本质是:线程在CPU中的执行流上下文(各寄存器和缓存中的内容)中带有锁标记,锁资源和线程的绑定关系体现在操作系统的内核层面

  • 线程解锁的本质是将特定的寄存器中的锁标记1交换回内存中的的锁变量(值为0)中

  • 上述的0/1标记位的交换过程是在一条汇编语句中完成的,保证了加锁和解锁过程的原子性,因而是线程安全的
    在这里插入图片描述
    在这里插入图片描述

  • 当内存中的锁变量为0时,其他线程申请锁时就会进入等待队列中休眠直到申请到锁后才能继续执行后续代码,从而在多线程环境中保证了加锁代码段的串行执行.

pthread线程库提供的锁操作

  • 定义全局的锁变量并初始化:
    • pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
  • 代码段的加锁和解锁:
    //代码段加锁,防止线程重入
    pthread_mutex_lock(&lock);

    //临界区代码段,同一时刻只能有一个线程在执行

    //代码段解锁,防止线程重入
    pthread_mutex_unlock(&lock);
  • 加锁后的模拟抢票代码:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 加锁后,线程等待休眠的可能性增大了,为了保证效率和系统并发量,保证线程安全的前提下,加锁的临界区中的代码量应尽可能少

三.死锁问题

  • 一种常见的死锁情况是:当各线程等待锁资源的逻辑链出现回路时,发生死锁
时间线程1线程2
1申请锁1(申请成功)
2申请锁2(申请成功)
3申请锁2(等待锁资源)
4申请锁1(等待锁资源)(死锁)
5
6

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

[Flutter] extends、implements、mixin和 abstract、extension的使用介绍说明

类创建&#xff1a;abstract&#xff08;抽象类&#xff09;、extension&#xff08;扩展&#xff09; 1.abstract&#xff08;抽象类&#xff09; dart 抽象类主要用于定义标准&#xff0c;子类可以继承抽象类&#xff0c;也可以实现抽象类接口。抽象类通过abstract 关键字来…

NLP论文阅读记录 - 2023 | EXABSUM:一种新的文本摘要方法,用于生成提取和抽象摘要

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.相关工作三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结思考 前言 EXABSUM: a new text summarization approach for generating ex…

PLC控制脉冲轴绝对位置往复运动(三菱FX系列简单状态机编程)

有关状态机的具体介绍,专栏有很多文章,大家可以通过下面的链接查看: https://rxxw-control.blog.csdn.net/article/details/125488089https://rxxw-control.blog.csdn.net/article/details/125488089三菱FX系列回原功能块介绍 https://rxxw-control.blog.csdn.net/article…

springboot注解@PropertySource作用

简介 PropertySource 是 Spring 框架中的一个注解&#xff0c;用于指定一个或多个属性文件&#xff08;通常是.properties文件&#xff09;这些文件包含了应用程序需要的配置信息。当你在 Spring 的配置类中使用此注解时&#xff0c;Spring 容器会加载这些属性文件&#xff0c…

中科星图——Landsat9_C2_SR大气校正后的地表反射率数据

数据名称&#xff1a; Landsat9_C2_SR 数据来源&#xff1a; USGS 时空范围&#xff1a; 2022年1月-2023年3月 空间范围&#xff1a; 全国 数据简介&#xff1a; Landsat9_C2_SR数据集是经大气校正后的地表反射率数据&#xff0c;属于Collection2的二级数据产品&#…

深入理解 Flink(二)Flink StateBackend 和 Checkpoint 容错深入分析

Flink State 设计详解 State 简单说&#xff0c;就是 Flink Job 的 Task 在运行过程中&#xff0c;产生的一些状态数据。这些状态数据&#xff0c;会辅助 Task 执行某些有状态计算&#xff0c;同时也涉及到 Flink Job 的重启状态恢复。所以&#xff0c;保存和管理每个 Task 的状…

如何制作网址链接活码?网址二维码生成器的使用方法

将网址转二维码图片来使用&#xff0c;是现在很常用的一种二维码类型&#xff0c;一般网址可以根据自己的用途来制作静态码或者活码两种形式。其中静态码只是单纯将网址链接转换成二维码&#xff0c;无法统计与修改&#xff0c;而生成网址活码可以在二维码图片不变情况下替换其…

114.QTimer类和QWidget类

目录 一、QTimer类 定时器使用举例&#xff1a; 二、QWidget类 2.1设置父对象 2.2窗口位置 2.3窗口尺寸 2.4窗口标题和图标 2.5信号 2.6槽函数 示例代码&#xff1a; 一、QTimer类 QTimer 是 Qt 中用于实现定时器的类。它可以在一定的时间间隔内发射信号&#xff0c;…

【小程序开发需要多少钱?】

哈喽&#xff0c;大家好&#xff0c;这里是智创开发。 我们今天聊聊开发一个小程序需要多少钱。 由于自己组建团队来开发小程序成本过高&#xff0c;大品牌的企业一般都不会这么搞&#xff0c;所以我们今天只谈假如我有需求&#xff0c;找服务商来全程搞定的费用大致是多少。和…

Flutter之运行错误:this and base files have different roots

运行时报错&#xff1a; this and base files have different roots: E:\Demolpro\waqu\build\flutter-plugin-_android_lifecycle and C:\Users\78535\AppData\Local\Pub\Cache\hosted\pub.dev\flutter_pulgin_android_lifecycle-2.0.17\android 如图&#xff1a; 这种情况…

半导体抛光用PFA容量瓶耐强酸碱定容瓶

PFA容量瓶又称可溶性聚四氟乙烯容量瓶、特氟龙容量瓶容量瓶&#xff0c;我司新推出螺纹和插口两种可供选择&#xff0c;目前有10ml、25ml、50ml、100ml、250ml、500ml、1000ml的规格可提供&#xff0c;产品质量有保障。 Teflon系列PFA容量瓶是一个透明的长颈瓶&#xff0c;瓶体…

水果音乐编曲软件 FL Studio v21.2.2.3914 中文免费版(附中文设置教程)

FL studio21中文别名水果编曲软件&#xff0c;是一款全能的音乐制作软件&#xff0c;包括编曲、录音、剪辑和混音等诸多功能&#xff0c;让你的电脑编程一个全能的录音室&#xff0c;它为您提供了一个集成的开发环境&#xff0c;使用起来非常简单有效&#xff0c;您的工作会变得…

公司运营数据分析大屏:引领企业决策,驱动业务增长

在数字化时代&#xff0c;数据已经成为企业决策的关键。为了更好地洞察市场趋势、优化业务流程、提升运营效率&#xff0c;越来越多的企业开始引入数据分析大屏以分析公司运营状况。这一创新举措不仅改变了传统的管理模式&#xff0c;更引领企业迈向智能化决策的新篇章。 公司运…

Spring之AOP源码(二)

书接上文 文章目录 一、简介1. 前文回顾2. 知识点补充 二、ProxyFactory源码分析1. ProxyFactory2. JdkDynamicAopProxy3. ObjenesisCglibAopProxy 三、 Spring AOP源码分析 一、简介 1. 前文回顾 前面我们已经介绍了AOP的基本使用方法以及基本原理&#xff0c;但是还没有涉…

在Linux下配置Apache HTTP服务器

在Linux的世界里&#xff0c;如果说有什么比解决各种“神秘”的故障更让人头疼&#xff0c;那一定就是配置Apache HTTP服务器了。这不是因为Apache有什么问题&#xff0c;而是因为配置它简直就像解谜游戏&#xff0c;一不留神就会让你陷入无尽的纠结。 首先&#xff0c;你需要…

Maya参考图的导入和层的应用

参考视频&#xff1a;08.参考图的导入和层的应用_哔哩哔哩_bilibili 前视图/右视图模式下导入图形 创建图层 锁定后可以避免图片位置的移动 前视图和右视图要根据参照物对齐 与模型保持一定距离&#xff0c;同时把该参照图添加到图层中 模型可以添加到图层2中

瑞吉外卖笔记系列(2) —— 完善员工的后台系统登录功能,实现员工信息管理

本文档主要 完善员工的后台系统登录功能&#xff0c;新增员工&#xff0c;员工信息分页查询&#xff0c;启用/禁用员工账号&#xff0c;编辑员工信息 一、完善后台系统登录功能 1.1 问题分析 目前存在的问题是&#xff1a;理论上&#xff0c;用户必须在 http://localhost:808…

LeetCode 590. N 叉树的后序遍历

590. N 叉树的后序遍历 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 后序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示&#xff0c;每组子节点由空值 null 分隔&#xff08;请参见示例&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [1,null,…

数据结构与算法:插入排序希尔排序

数据结构与算法&#xff1a;插入排序&希尔排序 插入排序希尔排序 插入排序 假设现在你有一个有序的数组&#xff0c;你要把一个数据插入到数组中&#xff0c;保证插入后依然有序&#xff0c;要怎么做&#xff1f; 对于人来说&#xff0c;这个问题就像是在整理扑克牌&…

优化 - 重构一次Mysql导致服务器的OOM

概述 优化了一次前后端处理不当导致的CPU的一次爆机行为&#xff0c;当然&#xff0c;这和服务器的配置低也有着密不可分的关系&#xff0c;简单的逻辑学告诉我们&#xff0c;要找到真正的问题&#xff0c;进行解决&#xff0c;CPU爆机的关键点在于前后端两个方面&#xff0c;…