当前位置: 首页 > news >正文

【Linux】线程安全及锁的使用

文章目录

  • 前言
  • 一、锁
    • 1.定义一个锁变量
    • 2.pthread_mutex_init
    • 3.pthread_mutex_destroy
    • 4.pthread_mutex_lock/pthread_mutex_unlock
    • 5.静态变量锁和全局变量锁的初始化
  • 二、问题描述及锁的运用
  • 三、RAII风格的锁


前言

临界资源: 在多个线程或进程间共享的资源.
临界区: 代码中访问临界资源的那部分代码区域.
多个线程同时访问共享数据, 其中至少一个线程进行了写操作, 且没有适当的同步机制来保护数据, 可能导致数据的不一致性, 也就是一种线程安全问题.

一、锁

多个线程同时访问共享数据, 其中至少一个线程进行了写操作, 且没有适当的同步机制来保护数据, 可能导致数据的不一致性, 对于这种问题, 可以通过加锁来解决, 加锁后, 只有申请到锁的线程才能进入临界区执行代码语句, 其他没有竞争到锁的线程会阻塞等待, 直到申请到锁的线程将锁释放, 接着各个线程再来竞争锁.
相当于被加锁的代码区域只能被各个线程串行执行.

1.定义一个锁变量

头文件: #include <pthread.h>

pthread_mutex_t mutex;

2.pthread_mutex_init

头文件: #include <pthread.h>
函数声明: int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

  • 返回值: 如果成功, 返回值为 0, 如果失败, 返回一个非零错误码.
  • mutex: 指向要初始化的互斥锁的指针.
  • attr: 设置互斥锁的属性, 一般传递 nullptr, 表示使用默认属性.

功能: 初始化锁.

示例代码:

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, nullptr);

3.pthread_mutex_destroy

头文件: #include <pthread.h>
函数声明: int pthread_mutex_destroy(pthread_mutex_t *mutex);

  • 返回值: 如果成功, 返回值为 0, 如果失败, 返回一个非零错误码.
  • mutex: 指向要释放的互斥锁的指针.

功能: 释放锁.

示例代码:

pthread_mutex_t mutex;
pthread_mutex_destroy(&mutex);

4.pthread_mutex_lock/pthread_mutex_unlock

头文件: #include <pthread.h>
函数声明: int pthread_mutex_lock(pthread_mutex_t *mutex); / int pthread_mutex_unlock(pthread_mutex_t *mutex);

  • 返回值: 如果成功, 返回值为 0, 如果失败, 返回一个非零错误码.
  • mutex: 指向已初始化的互斥锁的指针.

功能: 加锁/解锁.

示例代码:

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, nullptr);
pthread_mutex_lock(&mutex);
//...
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex);

在 pthread_mutex_lock(&mutex); 和 pthread_mutex_unlock(&mutex); 之间的代码就是被加锁的代码, 只有申请到锁的线程才能进入执行, 释放锁后, 各个线程再重新竞争锁.

5.静态变量锁和全局变量锁的初始化

如果锁变量是 static 修饰的静态变量锁或者是声明在全局的锁, 可以直接通过宏进行初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

二、问题描述及锁的运用

假设多个线程的入口函数为同一个, 代码如下:

#include <iostream>
#include <pthread.h>
using namespace std;void* Routine(void* arg)
{int cnt = 0;for(int i = 0; i < 3; ++i){printf("线程[%lld]中的cnt: %d, 地址为: 0x%x\n", pthread_self(), ++cnt, &cnt);}cout << endl;
}int main()
{pthread_t tids[3];for(int i = 0; i < 3; ++i){pthread_create(tids + i, nullptr, Routine, nullptr);}for(int i = 0; i < 3; ++i){pthread_join(tids[i], nullptr);}return 0;
}

可以看到, 在入口函数中, 存在一个变量 cnt, 那么多个线程的入口函数都是同一个, 每个线程都对该入口函数里的 cnt 进行累加操作, 是否会出现线程安全问题呢?

运行结果:
在这里插入图片描述
这里是不存在线程安全问题的, 因为每个线程在线程库中都存在自己的结构及数据上下文:
在这里插入图片描述
所以在入口函数中的变量等数据都是每个线程私有的一部分, 因此并不会有线程安全问题.

但是如果是全局变量或者被 static 修饰的变量, 被多个线程同时访问修改时就会存在线程安全问题, 假设一个场景: 三个线程进行"抢票", 即对全局变量 tickets 进行减减操作, 代码如下:

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <pthread.h>
using namespace std;int tickets = 30; //总票数void* Ticket(void* arg)
{while(1){//还有票if(tickets > 0){usleep(100000); //休眠0.1秒,模拟抢票前戏printf("线程 0x%x 抢到了一张票,还剩 %d 张票...\n", pthread_self(), --tickets);}else{break;}}return nullptr;
}int main()
{pthread_t tids[3];for(int i = 0; i < 3; ++i){pthread_create(tids + i, nullptr, Ticket, nullptr);}for(int i = 0; i < 3; ++i){pthread_join(tids[i], nullptr);}return 0;
}

运行结果:
在这里插入图片描述
可以看到, 在不加锁的情况下, tickets 直接被干到负数了, 原因就在于当 tickets 只剩 1 张时, 进行 if(tickets > 0) 判断时, 三个线程并行的执行了判断且进入了 if 语句内, 那 tickets 被干到负数也是必然的了.

通过加锁来规避这个问题, 代码修改如下:

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <pthread.h>
using namespace std;int tickets = 10000; //总票数
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void* Ticket(void* arg)
{while(1){pthread_mutex_lock(&mutex);//还有票if(tickets > 0){usleep(50000); //休眠0.05秒,模拟抢票前戏printf("线程 0x%x 抢到了一张票,还剩 %d 张票...\n", pthread_self(), --tickets);pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}}return nullptr;
}int main()
{pthread_t tids[3];for(int i = 0; i < 3; ++i){pthread_create(tids + i, nullptr, Ticket, nullptr);}for(int i = 0; i < 3; ++i){pthread_join(tids[i], nullptr);}pthread_mutex_destroy(&mutex);return 0;
}

需要注意的是 if 和 else 语句中都需要进行解锁, 如果只在 if 中解锁, 最后申请到锁但走了 else 语句的线程就并没有释放锁, 可能导致其他在等待锁资源的线程永远阻塞申请不到锁.

运行结果:
在这里插入图片描述

三、RAII风格的锁

虽然加锁, 解锁配套使用不难, 但毕竟是人写的, 难免可能出现加锁后忘了解锁, 而后导致死锁的情况, 所以通过 RAII 设计风格封装一下锁的使用, 可以有效避免这种问题.

示例代码:

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <pthread.h>
using namespace std;int tickets = 100; //总票数
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;class LockGuard 
{
public://构造加锁LockGuard(pthread_mutex_t* m): _pmutex(m){pthread_mutex_lock(_pmutex);}//析构解锁~LockGuard (){pthread_mutex_unlock(_pmutex);}
public:pthread_mutex_t* _pmutex;
};void* Ticket(void* arg)
{while(1){usleep(100000);LockGuard lg(&mutex);//还有票if(tickets > 0){usleep(50000); //休眠0.05秒,模拟抢票前戏printf("线程 0x%x 抢到了一张票,还剩 %d 张票...\n", pthread_self(), --tickets);}else{break;}}return nullptr;
}int main()
{pthread_t tids[3];for(int i = 0; i < 3; ++i){pthread_create(tids + i, nullptr, Ticket, nullptr);}for(int i = 0; i < 3; ++i){pthread_join(tids[i], nullptr);}pthread_mutex_destroy(&mutex);return 0;
}

运行结果:
在这里插入图片描述
这样可以达到, 构造时加锁, 析构时解锁, 全自动化, 不必担心忘了解锁了.

相关文章:

【Linux】线程安全及锁的使用

文章目录 前言一、锁1.定义一个锁变量2.pthread_mutex_init3.pthread_mutex_destroy4.pthread_mutex_lock/pthread_mutex_unlock5.静态变量锁和全局变量锁的初始化 二、问题描述及锁的运用三、RAII风格的锁 前言 临界资源: 在多个线程或进程间共享的资源. 临界区: 代码中访问临…...

深入解析绘图范式:面向对象与直接操作的较量

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 第一节&#xff1a;面向对象绘图的魅力 第二节&#xff1a;直接操作绘图模块的便捷性 第三…...

英特尔LLM技术挑战记录

英特尔技术介绍&#xff1a; Flash Attention Flash Attention 是一种高效的注意力机制实现&#xff0c;旨在优化大规模 Transformer 模型中的自注意力计算。在深度学习和自然语言处理领域&#xff0c;自注意力是 Transformer 架构的核心组件&#xff0c;用于模型中不同输入元…...

在 MFC 中 UNICODE 加 _T 与 L 长字符串,有什么区别?

在MFC&#xff08;Microsoft Foundation Classes&#xff09;和更广泛的Windows编程环境中&#xff0c;UNICODE宏用于指示程序应使用Unicode字符集&#xff08;通常是UTF-16&#xff09;来处理文本。当定义了UNICODE宏时&#xff0c;编译器和库函数会期待和处理宽字符&#xff…...

synopsys EDA 2016 合集 下载

包含如下安装包&#xff0c;如需安装服务也可联系我 FineSim_vL_2016.03 Laker201612 Library Compiler M-2016.12 Update Training PrimeTime M-2016.12 Update Training StarRC M-2016.12 Update Training SynopsysInstaller_v3.3 TSMC-65nm(OA) fm_vL-2016.03-SP1 fpga_vL-…...

CentOS 7如何使用systemctl管理应用

说明&#xff1a;本文介绍如何使用systemctl命令的方式来启动、查看、停止和重启应用&#xff0c;以安装后的prometheus、alertmanager为例&#xff1b; Step1&#xff1a;创建文件 在系统/etc/systemd/system/路径下&#xff0c;创建一个xxx.service文件&#xff0c;该文件内…...

武大深度学习期末复习-常见神经网络概念

深度学习经典神经网络概念、优缺点及应用场景 文章目录 一、多层感知机&#xff08;MLP&#xff09;1.1 结构和原理1.2 优缺点1.3 应用场景 二、卷积神经网络&#xff08;CNN&#xff09;2.1 结构和原理2.2 优缺点2.3 应用场景 三、循环神经网络&#xff08;RNN&#xff09;3.1…...

Leetcode3161. 物块放置查询(Go语言的红黑树 + 线段树)

题目截图 题目分析 每次1操作将会分裂成两块区间长度&#xff0c;以最近右端点记录左侧区间的长度即可 因此涉及到单点更新和区间查询 然后左右侧最近端点则使用redBlackTree&#xff0c;也就是python中的sortedlist ac code type seg []int// 把 i 处的值改成 val func (t …...

基于springboot实现医疗挂号管理系统项目【项目源码+论文说明】

基于springboot实现医疗挂号管理系统演示 摘要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&…...

ScrumMaster认证机构及CSM、PSM、RSM价值比较

企业现有的经营管理模式和传统的瀑布式交付模式&#xff0c;已经不能适应快速变化的市场响应和客户需求&#xff0c;现代的敏捷工作方式在过去数年涌现&#xff0c;比如Scrum&#xff0c;XP&#xff0c;看板&#xff0c;DevOps等敏捷方法&#xff0c;近十年Scrum在国内企业中备…...

加氢站压缩液驱比例泵放大器

加氢站压缩液驱液压系统的要求是实现换向和速度控制&#xff0c;对液压动力机构而言&#xff0c;按原理可区分为开式&#xff08;阀控&#xff09;- 节流控制系统和闭式&#xff08;泵控&#xff09;- 容积控制系统&#xff1a; 阀控系统 – 节流调速系统&#xff1a;由BEUEC比…...

MyBatis系统学习篇 - MyBatis逆向工程

MyBatis的逆向工程是指根据数据库表结构自动生成对应的Java实体类、Mapper接口和XML映射文件的过程。逆向工程可以帮助开发人员快速生成与数据库表对应的代码&#xff0c;减少手动编写重复代码的工作量。 我们在MyBatis中通过逆向工具来帮我简化繁琐的搭建框架&#xff0c;减少…...

SpringCloud的Config配置中心,为什么要分Server服务端和Client客户端?

SpringCloud的Config配置中心&#xff0c;为什么要分Server服务端和Client客户端&#xff1f; 在SpringCloud的Config配置中心中分了Server服务端和Client客户端&#xff0c;为什么需要这样分呢&#xff1f;它的思想是所有微服务的配置文件都放到git远程服务器上&#xff0c;让…...

「数据结构」队列

目录 队列的基本概念 队列的实现 头文件queue.h 实现函数接口 1.初始化和销毁 2.出队列和入队列 3.获取队头元素和队尾元素 4.队列长度判空 后记 前言 欢迎大家来到小鸥的博客~ 个人主页&#xff1a;海盗猫鸥 本篇专题&#xff1a;数据结构 多谢大家的支持啦&#xff…...

Python01 注释,关键字,变量,数据类型,格式化输出

# 导入模块 import keyword# 我的第一个Python程序 这是单行注释 快捷键&#xff1a;CTRL/这里是多行注释 可以写多行&#xff0c;用 三个单引号 包起来print(Hello work) print(你好&#xff0c;中国)aa 这是不是注释了&#xff0c;是多行文本。print(aa)# 快速创建 python …...

基于单片机智能防触电装置的研究与设计

摘 要 &#xff1a; 针对潮湿天气下配电线路附近易发生触电事故等问题 &#xff0c; 对单片机的控制算法进行了研究 &#xff0c; 设 计 了 一 种 基 于 单片机的野外智能防触电装置。 首先建立了该装置的整体结构框架 &#xff0c; 再分别进行硬件设计和软件流程分析 &#xf…...

机械行业工程设计资质乙级需要哪些人员

申请机械行业工程设计资质乙级需要的人员主要包括以下几个方面&#xff0c;具体要求和数量根据参考文章归纳如下&#xff1a; 一、主要专业技术人员 数量要求&#xff1a;主要专业技术人员数量应不少于所申请行业资质标准中主要专业技术人员配备表规定的人数。学历和职称要求…...

vivado改变波形图窗口颜色

点击右上角的设置图标 翻译对照...

蓝桥杯练习系统(算法训练)ALGO-932 低阶行列式计算

资源限制 内存限制&#xff1a;64.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 给出一个n阶行列式(1<n<9)&#xff0c;求出它的值。 输入格式 第一行给出两个正整数n,p&#xff1b;   接下来n行&…...

四川古力未来科技抖音小店安全靠谱,购物新体验

在数字化浪潮席卷而来的今天&#xff0c;电商行业蓬勃发展&#xff0c;各种线上购物平台如雨后春笋般涌现。其中&#xff0c;抖音小店凭借其独特的短视频直播购物模式&#xff0c;迅速赢得了广大消费者的青睐。而四川古力未来科技抖音小店&#xff0c;更是以其安全靠谱、品质保…...

3分钟高效掌握Python手机号查QQ号实用技巧

3分钟高效掌握Python手机号查QQ号实用技巧 【免费下载链接】phone2qq 项目地址: https://gitcode.com/gh_mirrors/ph/phone2qq 手机号查QQ号是现代社交网络管理中的一项实用技能&#xff0c;通过Python工具可以快速实现手机号与QQ号的关联查询。这个开源项目提供了一个…...

PCB设计前必知:五种生产工艺方案选型与Layout实战指南

1. 项目概述&#xff1a;PCB设计前必须厘清的生产工艺抉择画板子&#xff0c;尤其是电源板&#xff0c;画得再漂亮&#xff0c;如果生产不出来或者生产成本高得离谱&#xff0c;那一切努力都白费。我见过不少新手工程师&#xff0c;甚至是有些经验的老手&#xff0c;在PCB Layo…...

MAA智能助手:5分钟掌握《明日方舟》全自动日常管理终极方案

MAA智能助手&#xff1a;5分钟掌握《明日方舟》全自动日常管理终极方案 【免费下载链接】MaaAssistantArknights 《明日方舟》小助手&#xff0c;全日常一键长草&#xff01;| A one-click tool for the daily tasks of Arknights, supporting all clients. 项目地址: https:…...

从零到精通:Unity Timeline信号(Signal)与自定义轨道(Playable Track)的保姆级教程

从零到精通&#xff1a;Unity Timeline信号与自定义轨道实战指南 在Unity中制作电影级过场动画时&#xff0c;Timeline无疑是开发者最强大的工具之一。但许多开发者仅仅停留在基础动画剪辑的层面&#xff0c;未能充分挖掘其深度交互潜力。本文将带您突破常规用法&#xff0c;探…...

Video2X:你的AI视频画质修复专家,让老旧视频重获新生

Video2X&#xff1a;你的AI视频画质修复专家&#xff0c;让老旧视频重获新生 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trendin…...

【AI编程生产力跃迁】:用Perplexity秒级获取可运行代码示例的6大权威提示工程模板

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Perplexity代码示例查询的核心机制与能力边界 Perplexity 在处理代码示例查询时&#xff0c;并非依赖静态模板匹配&#xff0c;而是通过多阶段语义理解与上下文感知检索协同实现&#xff1a;首先对用户自然语…...

Artisan烘焙软件:基于Python的开源咖啡烘焙控制与数据分析平台

Artisan烘焙软件&#xff1a;基于Python的开源咖啡烘焙控制与数据分析平台 【免费下载链接】artisan artisan: the worlds most trusted roasting software 项目地址: https://gitcode.com/gh_mirrors/ar/artisan Artisan是一款采用Python技术栈构建的开源咖啡烘焙控制软…...

加热套、半导体加热带、工业加热夹克是同一种东西吗?

首先明确这个答案是肯定的&#xff0c;&#xff0c;这三种名称指同一种产品。作为北京龙腾圣华&#xff08;LOTUSANA&#xff09;的技术人员&#xff0c;我常被客户问到这个问题。我司自2002 年成立之初便自主研发投产此类柔性温控产品&#xff0c;最早行我们定名为加热套&…...

终极指南:如何使用AntiDupl.NET快速清理重复图片,释放硬盘空间

终极指南&#xff1a;如何使用AntiDupl.NET快速清理重复图片&#xff0c;释放硬盘空间 【免费下载链接】AntiDupl A program to search similar and defect pictures on the disk 项目地址: https://gitcode.com/gh_mirrors/an/AntiDupl 你是否曾因电脑中堆积如山的重复…...

别再只会用pandas了!用openpyxl的load_workbook处理Excel,这些坑我帮你踩过了

别再只会用pandas了&#xff01;用openpyxl的load_workbook处理Excel&#xff0c;这些坑我帮你踩过了 当Python开发者需要处理Excel文件时&#xff0c;pandas往往是首选工具——它简单、高效&#xff0c;能快速完成数据导入导出。但当你面对复杂格式的Excel文件&#xff0c;比…...