线程安全--互斥锁

news/2024/11/23 3:54:58/

在这里插入图片描述

文章目录

  • 一.线程安全问题
      • 读取无效(脏)数据
      • 丢失更新
      • 线程安全的保证--操作的原子性
  • 二.互斥锁及其实现原理
    • 互斥锁的实现原理
    • 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.ppmy.cn/news/1302401.html

相关文章

第8章-第6节-Java中字符流的缓冲流

1、在说正题之前&#xff0c;先说一个小细节&#xff0c;不管是字节流还是字符流都要注意这个细节&#xff0c;具体看这篇博文&#xff1a;关于Java的IO流里面的方法read()的小细节 2、字符流的缓冲流&#xff1a; 1&#xff09;、BufferedWriter 方法名说明void newLine()写…

一文教你如何在ThinkPHP6中轻松搞定审核流程管理!

随着互联网的发展&#xff0c;越来越多的企业开始使用网络进行业务处理&#xff0c;这就要求企业必须有一套完善的审核流程管理系统来确保业务的安全和规范。在PHP开发中&#xff0c;ThinkPHP6框架提供了便捷的审核流程管理功能&#xff0c;本文将介绍如何在ThinkPHP6中实现审核…

二叉树遍历C++

假设二叉树上各结点的权值互不相同且都为正整数。 给定二叉树的后序遍历和中序遍历&#xff0c;请你输出二叉树的前序遍历的最后一个数字。 输入格式 第一行包含整数 N&#xff0c;表示二叉树结点总数。 第二行给出二叉树的后序遍历序列。 第三行给出二叉树的中序遍历序列。 …

150套简约流行国内外优秀网页模板打包 /个人主页网站html模板 /html+css网页设计源码(分享)

这里把自己收藏的最新150套简约流行国内外优秀网页模板打包分享给大家&#xff0c;如果有用请点赞收藏&#xff0c;无密源码&#xff0c;直接拿来就可以用的。它是htmlcss网页设计源码&#xff0c;html5网页静态模板。 我分了品类&#xff0c;按行业或应用场景&#xff0c;不但…

在vue3和上挂载方法,以及在页面中怎么使用原型(公共)上的方法

//新建的项目的main.js文件是这样的 //main.js 文件 //befor import { createApp } from vue; import App from ./App.vue;const app createApp(App); app.mount(#app);以下例子用于解释在vue3.0的main.js中挂载公共的方法&#xff08;foo&#xff09; //main.js 文件 //afte…

PingCAP 受邀参加 FICC 2023,获 Open100 世纪全球开源贡献奖

2023 年 12 月&#xff0c;2023 国际测试委员会智能计算与芯片联邦大会&#xff08;FICC 2023&#xff09;在海南三亚举办&#xff0c;中外院士和数十位领域专家莅临出席。 大会现场 &#xff0c;开放源代码促进会创始人 Bruce Perens 颁发了 Open100 世纪全球开源贡献奖&…

vue3的福音框架arco.design

前言&#xff1a; 在vue2于2023年底正式宣布不在维护&#xff0c;vue3使用越来越频繁的时刻&#xff0c;我们实现项目的辅助框架也越来越多。element, iview, antd 等经典框架继续风靡一时&#xff0c;不过也有很多好的框架&#xff0c;功能也强大&#xff0c;比如我们今天说的…

VMware workstation安装debian-12.1.0虚拟机并配置网络

VMware workstation安装debian-12.1.0虚拟机并配置网络 Debian 是一个完全自由的操作系统&#xff01;Debian 有一个由普罗大众组成的社区&#xff01;该文档适用于在VMware workstation平台安装debian-12.1.0虚拟机。 1.安装准备 1.1安装平台 Windows 11 1.2软件信息 软…