【Linux C | 多线程编程】线程同步 | 互斥量(互斥锁)介绍和使用
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:
本文未经允许,不得转发!!!
目录
- 🎄一、概述
- 🎄二、为什么需要互斥量
- 🎄三、互斥量的使用
- ✨3.1 互斥量的初始化
- ✨3.2 互斥量的销毁
- ✨3.3 互斥量的加锁和解锁
- 🎄四、互斥量的属性
- 🎄五、总结
![]()
🎄一、概述
互斥量采用的是英文mutual exclusive(互相排斥之意)的缩写,即mutex,是多线程编程中,常用来进行同步访问的方式之一。根据互斥量的用法,可以形象地将互斥量比喻成一把锁,锁住关键代码(临界区),每次只允许一个线程进入。
互斥量的工作机制:互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程在次被阻塞,等待下次运行状态。
![]()
🎄二、为什么需要互斥量
大部分情况下, 线程使用的数据都是局部变量, 变量的地址在线程栈空间内, 这种情况下, 变量归属于单个线程, 其他线程无法获取到这种变量。但多数的多线程编程种,会出现一些资源是多个线程共享使用的,如:全局变量、堆空间指针变量等。
当多个线程可以同时改变某个共享资源,并且这个改变的操作不是原子操作,而又不加限制的话,那么改变的结果可能是意想不到的。这就是需要互斥量的原因。
🌰看例子:下面例子,创建4个线程,对共享资源(g_Count全局变量)执行了1000万次自加1操作。我们期待的结果应该是4000万,但运行结果有时却非4000万。
// 08_mutex_test.c
// gcc 08_mutex_test.c -lpthread
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
int g_Count = 0;
void *func(void *arg)
{int i=0;for(i=0; i<10000000; i++){g_Count++;}return NULL;
}int main()
{// 创建4个线程pthread_t threadId[4];int i=0;for(i=0; i<4; i++){pthread_create(&threadId[i], NULL, func, NULL);}for(i=0; i<4; i++){pthread_join(threadId[i],NULL);printf("join threadId=%lx\n",threadId[i]);}printf("g_Count=%d\n",g_Count);return 0;
}
运行结果如下:执行三次,只出现了一次4000万,且每次结果都不一样。因为g_Count++;语句不是一个原子操作,假设4个线程同时获取到g_Count时值为1,4个线程都执行g_Count++后,每个线程都认为此时g_Count的值为2,但4个线程执行了4次了。

综上所述,当多个线程可以同时操作共享资源时,需要满足下面三点来使各个线程互斥:
1、当一个线程操作共享资源时,不允许其他线程同时操作该资源。
2、当线程不再操作共享资源时,不能阻碍其他线程操作该资源。
3、当多个线程同时操作一个共享资源时,只允许一个线程执行操作。
![]()
🎄三、互斥量的使用
正确地使用互斥量来保护共享数据,首先要定义和初始化互斥量。然后是使用互斥量的加锁、解锁来保护共享数据,最后使用完销毁互斥量。
✨3.1 互斥量的初始化
POSIX提供了两种初始化互斥量的方法。
-
1、是将
PTHREAD_MUTEX_INITIALIZER赋值给定义的互斥量,如下:#include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;但这个方法没办法设置互斥量的属性,也不适用于动态分配的互斥量,比较少用。
-
2、使用
pthread_mutex_init初始化互斥量。如下:#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);第二个
pthread_mutexattr_t指针的入参,是用来设定互斥量的属性的。大部分情况下,并不需要设置互斥量的属性,传递NULL即可,表示使用互斥量的默认属性。
调用pthread_mutex_init之后, 互斥量处于没有加锁的状态。
✨3.2 互斥量的销毁
使用pthread_mutex_init初始化的互斥量,在确定不再需要互斥量的时候, 就要销毁它。 在销毁之前, 有三点需要注意:
1、使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量无须销毁。
2、不要销毁一个已加锁的互斥量, 或者是真正配合条件变量使用的互斥量。
3、已经销毁的互斥量, 要确保后面不会有线程再尝试加锁。
销毁互斥量的接口如下:
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
当互斥量处于已加锁的状态, 或者正在和条件变量配合使用, 调用pthread_mutex_destroy函数会返回EBUSY错误码。
✨3.3 互斥量的加锁和解锁
关于互斥量的加锁和解锁,POSIX提供了如下接口:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock (pthread_mutex_t * mutex, const struct timespec *abstime);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
- pthread_mutex_lock:对互斥量加锁,如果互斥量未锁定,则将互斥量锁定, 同时返回成功。如果互斥量已经上锁,则一直阻塞等待互斥量解锁;
- pthread_mutex_trylock:对互斥量加锁,如果互斥量未锁定,则将互斥量锁定, 同时返回成功。如果互斥量已经上锁,则不会阻塞等待,直接返回
EBUSY; - pthread_mutex_timedlock:对互斥量加锁,如果互斥量未锁定,则将互斥量锁定, 同时返回成功。如果互斥量已经上锁,则等待
abstime设置的时间,如果还处于锁定状态,直接返回ETIMEOUT;注意,abstime是绝对时间,如果最多等待2分钟, 那么这个值应该是当前时间加上2分钟 - pthread_mutex_unlock:对互斥量解锁。
![]()
🎄四、互斥量的属性
线程和线程的同步对象(互斥量,读写锁,条件变量)都具有属性。在修改属性前都需要对该结构进行初始化。使用后要把该结构回收。大部分情况使用的都是默认属性。
互斥量的属性相关接口:
int pthread_mutexattr_init (pthread_mutexattr_t *attr); // 初始化互斥量属性为默认属性
int pthread_mutexattr_destroy (pthread_mutexattr_t *attr);// 销毁互斥量属性/* Get the process-shared flag of the mutex attribute ATTR. */
int pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr,int *pshared);/* Set the process-shared flag of the mutex attribute ATTR. */
int pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int shared)
attr中pshared属性表示用这个属性对象创建的互斥锁的作用域,它的取值可以是PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED。
- PTHREAD_PROCESS_PRIVATE:默认属性,只有和创建这个互斥锁的线程在同一个进程中的线程才能访问这个互斥锁;
- PTHREAD_PROCESS_SHARED:所创建的互斥锁将被保存在共享内存中,可以被多个进程中的线程共享。
互斥锁类型:
- PTHREAD_MUTEX_NORMAL;
- PTHREAD_MUTEX_ERRORCHECK;
- PTHREAD_MUTEX_RECURSIVE;
- PTHREAD_MUTEX_DEFAULT。
![]()
🎄五、总结
本文介绍了Linux系统下,多线程编程常用的互斥量,先是介绍了需要互斥量的原因,然后介绍互斥量的使用,并给出使用例子。
下面是使用互斥量对第二小节例子进行修改后的代码:
// 08_mutex_test.c
// gcc 08_mutex_test.c -lpthread
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
int g_Count = 0;
pthread_mutex_t g_mutex;
void *func(void *arg)
{int i=0;for(i=0; i<10000000; i++){pthread_mutex_lock(&g_mutex);g_Count++;pthread_mutex_unlock(&g_mutex);}return NULL;
}int main()
{pthread_mutex_init(&g_mutex, NULL);// 创建4个线程pthread_t threadId[4];int i=0;for(i=0; i<4; i++){pthread_create(&threadId[i], NULL, func, NULL);}for(i=0; i<4; i++){pthread_join(threadId[i],NULL);printf("join threadId=%lx\n",threadId[i]);}printf("g_Count=%d\n",g_Count);pthread_mutex_destroy(&g_mutex);return 0;
}

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
参考:
《linux_多线程》
《Linux环境编程:从应用到内核》
相关文章:
【Linux C | 多线程编程】线程同步 | 互斥量(互斥锁)介绍和使用
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 ⏰发布时间⏰: 本文未经允许…...
mid_360建图和定位
录制数据 roslaunch livox_ros_driver2 msg_MID360.launch使用fast-lio 建图 https://github.com/hku-mars/FAST_LIO.git 建图效果 使用python做显示 https://gitee.com/linjiey11/mid360/blob/master/show_pcd.py 使用 point_lio建图 https://github.com/hku-mars/Point…...
ThreadX在STM32上的移植:通用启动文件tx_initialize_low_level.s
在嵌入式系统开发中,实时操作系统(RTOS)的选择对于系统性能和稳定性至关重要。ThreadX是一种广泛使用的RTOS,它以其小巧、快速和可靠而闻名。在本文中,我们将探讨如何将ThreadX移植到STM32微控制器上,特别是…...
【python实战】游戏开发——恐龙跳跃小游戏
目录 背景开发环境步骤初步搭建主窗口的实现地平线滑动场景小恐龙跳跃障碍物出现碰撞检测和积分功能优化改为随时可以跳跃源码参考背景 小恐龙沿着地平线前进,遇到障碍物跳跃,躲避障碍物。根据躲避的障碍物进行积分统计。 开发环境 开发环境:Windows10 软件:pycharm 开发…...
成都百洲文化传媒有限公司电商领域的新锐力量
在电商服务领域,成都百洲文化传媒有限公司凭借其专业的服务理念和创新的策略,正逐渐成为行业内的翘楚。这家公司不仅拥有资深的电商团队,还以其精准的市场定位和高效的服务模式,赢得了众多客户的信赖和好评。 一、专业团队&#…...
1042: 中缀表达式转换为后缀表达式
解法:直接给算法 创建一个栈和一个空的后缀表达式字符串。 遍历中缀表达式中的每个字符。 如果当前字符是操作数,直接将其添加到后缀表达式字符串中。 如果当前字符是操作符,需要将其与栈顶的操作符进行比较: 如果栈为空&#…...
避免上下文切换--Linux原子函数
在现代操作系统中,原子函数是一类特殊的函数,它们能够保证在并发环境中执行的操作是不可分割的。这意味着一旦一个原子函数开始执行,它的操作会在任何其他线程或进程可以介入之前完全完成。这是通过多种机制实现的,包括硬件支持的…...
塔面板php7.37.4版本不支持ZipArchive手工安装扩展方法
centos 7 宝塔面板安装的PHP7.3和7.4默认已经不带zip扩展,要手工安装zip扩展首先需要安装libzip, yum -y install libzip 方法如下: 宝塔面板php7.3版本在SSH命令行界面执行以下语句: cd /www/server/php/73/src/ext/zip/ /ww…...
go语言并发实战——日志收集系统(一) 项目前言
-goroutine- 简介 go并发编程的练手项目 项目背景 一般来说业务系统都有自己的日志,当系统出现问题时,我们一般需要通过日志信息来定位与解决问题,当系统机器较少时我们可以登录服务器来查看,但是当系统机器较多时,我们通过服务器来查看日志的成本就会变得很大,…...
Android Studio 之 Intent及其参数传递
一、Intent 显式Intent:通过组件名指定启动的目标组件,比如startActivity(new Intent(A.this,B.class)); 每次启动的组件只有一个~隐式Intent:不指定组件名,而指定Intent的Action,Data,或Category,当我们启动组件时, 会去匹配AndroidManifest.xml相关组件的Intent-…...
【黑马头条】-day06自媒体文章上下架-Kafka
文章目录 今日内容1 Kafka1.1 消息中间件对比1.2 kafka介绍1.3 kafka安装及配置1.4 kafka案例1.4.1 导入kafka客户端1.4.2 编写生产者消费者1.4.3 启动测试1.4.4 多消费者启动 1.5 kafka分区机制1.5.1 topic剖析 1.6 kafka高可用设计1.7 kafka生产者详解1.7.1 同步发送1.7.2 异…...
非线性特征曲线线性化插补器(CODESYS 完整ST代码)
1、如何利用博途PLC和信捷PLC实现非线性特征曲线的线性化可以参考下面文章链接: 非线性特征曲线线性化(插补功能块SCL源代码+C代码)_scl直线插补程序-CSDN博客文章浏览阅读382次。信捷PLC压力闭环控制应用(C语言完整PD、PID源代码)_RXXW_Dor的博客-CSDN博客闭环控制的系列文章…...
vue3从精通到入门4:diff算法的实现
Vue 3 的 diff 算法相较于 Vue 2 有了一些改进和优化,主要是为了应对更复杂的组件结构和更高的性能需求。 以下是 Vue 3 diff 算法在处理列表更新时的大致步骤: 头头比较:首先,比较新旧列表的头节点(即第一个节点&…...
(三)C++自制植物大战僵尸游戏项目结构说明
植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/ErelL 一、项目结构 打开项目后,在解决方案管理器中有五个项目,分别是libbox2d、libcocos2d、librecast、libSpine、PlantsVsZombies五个项目,除PlantsVsZombies外,其他四个…...
动态规划专练( 279.完全平方数)
279.完全平方数 给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 …...
京东商品详情API接口(商品属性丨sku价格丨详情图丨标题等数据)
京东商品详情API接口是京东开放平台提供的一种API接口,通过调用该接口,开发者可以获取京东商品的标题、价格、库存、月销量、总销量、详情描述、图片等详细信息。下面针对您提到的商品属性、SKU价格、详情图以及标题等数据,做具体介绍&#x…...
Springboot+Vue项目-基于Java+MySQL的校园周边美食探索及分享平台系统(附源码+演示视频+LW)
大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &…...
折叠面板组件(vue)
代码 <template><div class"collapse-info"><div class"collapse-title"><div class"title-left">{{ title }}</div><div click"changeHide"> <Button size"small" v-if"sho…...
【Canvas技法】蓝底金字北岛诗节选(径向渐变色、文字阴影示例)
【效果图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>北岛诗选</title><style type"text/css">.c…...
【大语言模型】基础:TF-IDF
TF-IDF (Term Frequency-Inverse Document Frequency) 是一种用于信息检索与文本挖掘的统计方法,用来评估一个词对于一个文件集或一个语料库中的其中一份文件的重要性。它是一种常用于文本处理和自然语言处理的权重计算技术。 原理 TF-IDF 由两部分组成࿱…...
IT6500电源蜂鸣器太吵?教你用Python远程静音并实现电压步进扫描
IT6500电源蜂鸣器静音与电压步进扫描的Python实战指南 深夜的实验室里,IT6500电源的蜂鸣器突然响起,刺耳的"哔哔"声打破了宁静,这种场景对于电子工程师来说再熟悉不过。本文将带你用Python彻底解决这个恼人的问题,同时实…...
ComfyUI自定义节点开发指南:从零构建你的专属AI工具链
1. 为什么需要自定义ComfyUI节点? 第一次用ComfyUI做AI绘画时,我就被它灵活的节点式操作吸引了。但用着用着发现一个问题:官方提供的节点虽然强大,但总有些特殊需求无法满足。比如想给生成的图片自动打水印、批量处理文件夹里的图…...
Gpmall分布式事务处理:订单创建与库存扣减的最终一致性保障
Gpmall分布式事务处理:订单创建与库存扣减的最终一致性保障 【免费下载链接】gpmall 项目地址: https://gitcode.com/gh_mirrors/gp/gpmall 在电商系统中,订单创建与库存扣减的分布式事务处理是确保数据一致性的核心挑战。Gpmall项目通过创新的P…...
华三路由器远程管理全攻略:Telnet/SSH/FTP三种方式配置避坑指南
华三路由器远程管理全攻略:Telnet/SSH/FTP三种方式配置避坑指南 当你面对一台全新的华三路由器时,远程管理配置往往是第一个需要解决的问题。作为运维人员,我们既需要考虑操作便捷性,又必须兼顾安全性。本文将带你深入探索Telnet、…...
RevokeMsgPatcher:突破微信消息限制的高效管理工具
RevokeMsgPatcher:突破微信消息限制的高效管理工具 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁(我已经看到了,撤回也没用了) 项目地址: https://gitcode.com/G…...
PlayCover深度技术解析:如何在M系列Mac上实现iOS游戏原生运行体验
PlayCover深度技术解析:如何在M系列Mac上实现iOS游戏原生运行体验 【免费下载链接】PlayCover Community fork of PlayCover 项目地址: https://gitcode.com/gh_mirrors/pl/PlayCover PlayCover作为一款创新的开源工具,让Apple Silicon Mac用户能…...
Go语言的context.WithCancel取消信号传播与资源清理在分布式系统中的协调
Go语言的context.WithCancel取消信号传播与资源清理在分布式系统中的协调 在分布式系统中,任务的取消与资源清理是确保系统稳定性和高效性的关键挑战。Go语言通过context包提供了优雅的解决方案,尤其是context.WithCancel机制,能够实现跨组件…...
深度学习中的优化器:原理与实践
深度学习中的优化器:原理与实践 一、背景与动机 在深度学习中,优化器是模型训练的核心组件,它决定了模型参数如何根据损失函数的梯度进行更新。选择合适的优化器对于模型的训练速度和最终性能至关重要。本文将深入探讨各种优化器的核心原理、…...
保姆级避坑指南:Ubuntu系统下Hadoop HA集群搭建,我踩过的那些SSH和配置文件的“坑”
Ubuntu下Hadoop HA集群搭建:那些教科书不会告诉你的实战陷阱 第一次在Ubuntu上搭建Hadoop HA集群时,我天真地以为照着官方文档就能顺利跑起来。直到SSH连接莫名其妙失败、JournalNode权限报错刷屏、ZKFC死活不启动时,才明白为什么有人说大数据…...
FPGA调试避坑指南:Vivado ILA采样深度和探针位宽怎么设?资源占用与调试效果的平衡术
FPGA调试实战:ILA采样深度与探针位宽的黄金平衡法则 当你在Artix-7芯片上调试一个包含32位计数器和多状态机的设计时,突然发现ILA吃掉了一半的Block RAM资源,而采样深度却只够捕获5个时钟周期的数据——这种场景是否似曾相识?本文…...
