Linux操作系统7- 线程同步与互斥1(POSIX互斥锁的使用详解)

上篇文章:Linux操作系统6- 线程4(POSIX线程的简单封装)-CSDN博客

本篇代码仓库:myLerningCode/l30 · 橘子真甜/Linux操作系统与网络编程学习 - 码云 - 开源中国 (gitee.com)

目录

一. 线程不互斥造成的结果        

二. pthread_mutex_t 互斥锁操作

2.1 互斥锁的定义与初始化

a 动态分配初始化

b 静态分配

2.2  加解锁操作

三. 互斥锁的使用

四. 互斥锁总结

4.1 谁来保护锁?

4.2 加解锁的原子性问题 


一. 线程不互斥造成的结果        

        线程是进程内的执行流,是CPU调度的基本单位,线程之间有共享资源。比如代码区,常量区,数据段(.data和.bss)以及堆区,进程栈区的资源。

        加入一个线程A正要修改某一个全局变量的时候,因为CPU的调度离开了。然后另一个线程B来访问这个变量并且修改,线程A再次访问这个变量就会导致资源错误。

代码举例:

#include <iostream>
#include <memory>#include <unistd.h>
#include "Thread.hpp"
using namespace std;int tickets = 1000;void *get_tickets(void *args)
{string name = static_cast<const char *>(args);while (true){if (tickets > 0){usleep(1000); // 模拟抢票cout << "线程" << name << "正在获取票,票号为" << tickets-- << endl;}else{cout << "无票可抢" << endl;break;}}return nullptr;
}int main()
{std::unique_ptr<Thread> t1(new Thread(get_tickets, (void *)"thread 1", 0));std::unique_ptr<Thread> t2(new Thread(get_tickets, (void *)"thread 2", 0));std::unique_ptr<Thread> t3(new Thread(get_tickets, (void *)"thread 3", 0));std::unique_ptr<Thread> t4(new Thread(get_tickets, (void *)"thread 4", 0));t1->start();t2->start();t3->start();t4->start();t1->join();t2->join();t3->join();t4->join();return 0;
}

 运作结果如下:

 可以看到,线程取到了负数的票。

        这是因为--操作在汇编是三条指令

1 从内存读取到cpu中

2 cpu对根据命令执行计算和逻辑操作

3 将结果写回内存中

        如果线程A执行完命令1后就被切换走了,线程B执行了一个完整--操作。然后切换回线程A的时候,线程A根据上下文操作继续执行计算命令。这样就会导致一个数据被多次--了。从而导致数据错误

         保护共享资源最简单的方式就是对临界资源加锁

二. pthread_mutex_t 互斥锁操作

2.1 互斥锁的定义与初始化

a 动态分配初始化

//头文件
#include <pthread.h>//定义
pthread_mutex_t mutex;//动态分配
pthread_mutex_t mutex = INITIALIZER

        动态分配一般用于定义全局变量和静态变量,动态分配初始化的互斥锁不需要销毁。

b 静态分配

//头文件
#include <pthread.h>//定义
pthread_mutex_t mutex;//初始化函数
pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)//参数
mutex:输入输出型参数,调用该接口之后对mutex进行初始化
attr:用于控制锁的属性,一般设置为nulpptr//销毁锁
int pthread_mutex_destroy(pthread_mutex_t *restrict mutex)
//与初始化一样,用于销毁一个锁

注意静态分配的时候:

不要销毁已经加锁的锁

2 不要销毁了锁之后,继续对这个锁加锁

2.2  加解锁操作

        使用pthread_mutex_lock进行加锁 pthread_mutex_unlock进行解锁。

//所需头文件
#include <pthread.h>//用于对一个已经初始化的锁加锁
pthread_mutex_lock(pthread_mutex_t *mutex);
//所需头文件
#include <pthread.h>//对加锁的mutex进行解锁
pthread_mutex_unlock(pthread_mutex_t *mutex);

三. 互斥锁的使用

        我们对线程获取票这个操作进行加锁。如果我们定义的锁不是全局锁,需要通过传参的方式将锁传递给线程进行加锁解锁。当然全局锁就不需要了

这里以非全局锁举例

#include <iostream>
#include <vector>
#include <memory>#include <unistd.h>
#include "Thread.hpp"
#include <bits/unique_ptr.h>
using namespace std;const int thread_num = 5;
int tickets = 1000;struct ThreadData
{ThreadData(const string &name = string(), pthread_mutex_t *lock = nullptr): _name(name), _lock(lock) {}string _name;pthread_mutex_t *_lock;
};// 通过参数的方式传递锁
void *get_tickets(void *args)
{ThreadData *td = static_cast<ThreadData *>(args);string name = td->_name;pthread_mutex_t *mtx = td->_lock;while (true){// 加锁pthread_mutex_lock(mtx);if (tickets > 0){usleep(1000); // 模拟抢票cout << "线程" << name << "正在获取票,票号为" << tickets-- << endl;// 解锁pthread_mutex_unlock(mtx);}else{cout << "无票可抢" << endl;// 由于break会导致无法解锁,这里需要解锁pthread_mutex_unlock(mtx);break;}}return nullptr;
}int main()
{// 定义域初始化锁pthread_mutex_t lock;pthread_mutex_init(&lock, 0);vector<Thread> tds;for (int i = 0; i < thread_num; i++){string name = "thread-";name += to_string(i);ThreadData *td = new ThreadData(name, &lock);tds.push_back(Thread(get_tickets, td, 0));}for (int i = 0; i < tds.size(); i++)tds[i].start();for (int i = 0; i < tds.size(); i++)tds[i].join();// 销毁锁pthread_mutex_destroy(&lock);return 0;
}

运行结果如下:

        可以看到,通过加锁操作可以保护共享资源的访问 

        加锁解锁之间的区域称为临界区,线程在临界区需要访问的资源称为临界资源。加解锁虽然能保护线程访问资源,但是会加解锁操作和线程竞争锁会导致效率降低

四. 互斥锁总结

4.1 谁来保护锁?

        锁可以保护我们访问共享资源,但是锁本身也是一个共享资源,为什么我们加锁解锁不需要保护呢?因为加锁解锁操作是原子的,只需执行必定执行完

        申请锁成功的线程会继续执行代码,申请锁失败的线程在干什么?此时线程会阻塞等待加锁的线程释放锁。

        就像下图,只有一个线程在临界区中执行

     

4.2 加解锁的原子性问题 

        一个线程申请锁成功后,在临界区中能否被切换走?(可以切换走,并且其他线程仍无法访问临界区,因为我是带着锁走的,锁没有被释放)

        那么这样会不会导致程序的效率过低(其他线程都在阻塞等待)?会导致效率降低,所以在编码的时候尽量保证临界区比较小,将无关的代码放到临界区外部。

        即尽量保证锁的粒度比较小。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入 Linux 声卡驱动开发:核心问题与实战解析

1. 字符设备驱动如何为声卡提供操作接口&#xff1f; 问题背景 在 Linux 系统中&#xff0c;声卡被抽象为字符设备。如何通过代码让应用程序能够访问声卡的录音和播放功能&#xff1f; 核心答案 1.1 字符设备驱动的核心结构 Linux 字符设备驱动通过 file_operations 结构体定…

洛谷 [语言月赛 202503] 题解(C++)

本文为洛谷3月的语言月赛题目全部题解&#xff0c;难度为入门到普及-&#xff0c; 觉的有帮助或者写的不错的可以点个赞 题目链接为 题目列表 - 洛谷 | 计算机科学教育新生态 目录 题目A:长方形 解题思路: 代码(C): 题目B:水流 题目大意: 解题思路: 代码(C): 题目C:格…

算法每日一练 (15)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 算法每日一练 (15)第 N 个泰波那契数题目描述解题思路…

实验11 机器学习-贝叶斯分类器

实验11 机器学习-贝叶斯分类器 一、实验目的 &#xff08;1&#xff09;理解并熟悉贝叶斯分类器的思想和原理&#xff1b; &#xff08;2&#xff09;熟悉贝叶斯分类器的数学推导过程&#xff1b; &#xff08;3&#xff09;能运用贝叶斯分类器解决实际问题并体会算法的效果&a…

Matrix-breakout-2-morpheus靶机实战攻略

1.安装并开启靶机 2.获取靶机IP 3.浏览器访问靶机 4.扫描敏感目录文件和端口 gobuster dir -u http://192.168.52.135 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html 5.访问文件和端口 发现在graffiti.php输入框输入内容后页面会返回内容…

【知识】Graph Sparsification、Graph Coarsening、Graph Condensation的详细介绍和对比

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 1. 理论基础&#xff08;Definitions & Theoretical Background&#xff09; 2. 算法方法&#xff08;Techniques & Algorithms&#x…

Java单元测试、Junit、断言、单元测试常见注解、单元测试Maven依赖范围、Maven常见问题解决方法

一. 测试 1. 测试&#xff1a;是一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程 2. 阶段划分&#xff1a;单元测试、集成测试、系统测试、验收测试。 ① 单元测试&#xff1a;对软件的基本组成单位进行测试&#xff0c;最小测试单位&#xff1b;目的检验软件基本组…

【Notepad】Notepad优化笔记AutoHotkey语法高亮\设置替换默认的notepad程序\设置主题\增加返回上一个编辑地方插件

Npp使用优化笔记 AHK或自定义语法高亮设置替换系统默认的notepad设置主题返回上一次编辑的地方插件使用 AHK或自定义语法高亮 具体参考该论坛 https://www.autohotkey.com/boards/viewtopic.php?t50 设置替换默认的notepad程序 参考文章&#xff1a; https://www.winhelpo…

Mac:Maven 下载+安装+环境配置(详细讲解)

&#x1f4cc; 下载 Maven 下载地址&#xff1a;https://maven.apache.org/download.cgi &#x1f4cc; 无需安装 Apache官网下载 Maven 压缩包&#xff0c;无需安装&#xff0c;下载解压后放到自己指定目录下即可。 按我自己的习惯&#xff0c;我会在用户 jane 目录下新建…

[K!nd4SUS 2025] Crypto

最后一个把周末的补完。这个今天问了小鸡块神终于把一个补上&#xff0c;完成5/6&#xff0c;最后一个网站也上不去不弄了。 Matrices Matrices Matrices 这个是不是叫LWE呀&#xff0c;名词忘了&#xff0c;但意思还是知道。 b a*s e 这里的e是高斯分成&#xff0c;用1000…

学习threejs,构建THREE.ParametricGeometry参数化函数生成几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.ParametricGeometry1…

Canal 解析与 Spring Boot 整合实战

一、Canal 简介 1.1 Canal 是什么&#xff1f; Canal 是阿里巴巴开源的一款基于 MySQL 数据库增量日志解析&#xff08;Binlog&#xff09;中间件&#xff0c;它模拟 MySQL 的从机&#xff08;Slave&#xff09;行为&#xff0c;监听 MySQL 主机的二进制日志&#xff08;Binl…

【海螺AI视频】蓝耘智算 | AI视频新浪潮:蓝耘MaaS与海螺AI视频创作体验

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈人工智能与大模型应用 ⌋ ⌋ ⌋ 人工智能&#xff08;AI&#xff09;通过算法模拟人类智能&#xff0c;利用机器学习、深度学习等技术驱动医疗、金融等领域的智能化。大模型是千亿参数的深度神经网络&#xff08;如ChatGPT&…

Prometheus使用

介绍&#xff1a;Prometheus 是一个开源的 监控与告警系统&#xff0c;主要用于采集和存储时间序列数据&#xff08;Time Series Data&#xff09; Prometheus的自定义查询语言PromQL Metric类型 为了能够帮助用户理解和区分这些不同监控指标之间的差异&#xff0c;Prometheu…

Linux 文件操作-标准IO函数3- fread读取、fwrite写入、 fprintf向文件写入格式化数据、fscanf逐行读取格式化数据的验证

目录 1. fread 从文件中读取数据 1.1 读取次数 每次读取字节数 < 原内容字节数 1.2 读取次数 每次读取字节数 > 原内容字节数 2.fwrite 向文件中写入数据 2.1写入字符串验证 2.2写入结构体验证 3. fprintf 将数据写入到指定文件 4. fscanf 从文件中逐行读取内容…

再学:abi编码 地址类型与底层调用

目录 1.内置全局变量及函数 2.abi 3.地址类型 4.transfer 1.内置全局变量及函数 2.abi data就是abi编码 abi描述&#xff1a;以json格式表明有什么方法 3.地址类型 4.transfer x.transfer:合约转给x call 和 delegatecall 是 Solidity 中用于底层合约调用的函数&#xff0…

解决前端文字超高度有滚动条的情况下padding失效(el-scrollbar)使用

<div class"detailsBlocksContent"><div>测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试…

SpringCloud 学习笔记3(OpenFeign)

OpenFeign 微服务之间的通信方式&#xff0c;通常有两种&#xff1a;RPC 和 HTTP。 简言之&#xff0c;RPC 就是像调用本地方法一样调用远程方法。 在 SpringCloud 中&#xff0c;默认是使用 HTTP 来进行微服务的通信&#xff0c;最常用的实现形式有两种&#xff1a; RestTem…

c中<string.h>

常见错误与最佳实践 缓冲区溢出&#xff1a; strcpy 和 strcat 不检查目标缓冲区大小&#xff0c;需手动确保空间足够。替代方案&#xff1a;使用 strncpy 和 strncat&#xff0c;或动态分配内存&#xff08;如 malloc&#xff09;。 未终止的字符串&#xff1a; 确保字符串以…

C++动态规划从入门到精通

一、动态规划基础概念详解 什么是动态规划 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是一种通过将复杂问题分解为重叠子问题&#xff0c;并存储子问题解以避免重复计算的优化算法。它适用于具有以下两个关键性质的问题&#xff1a; 最优子结构&…