【Linux C | 多线程编程】线程同步 | 互斥量(互斥锁)介绍和使用

news2024/12/24 20:34:19

😁博客主页😁:🚀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)

attrpshared 属性表示用这个属性对象创建的互斥锁的作用域,它的取值可以是 PTHREAD_PROCESS_PRIVATEPTHREAD_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环境编程:从应用到内核》

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

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

相关文章

vmware esxi6.0安装配置操作

系统安装及配置 在服务器上安装ESXI 6.0 提示是否继续安装 如果不想安装,按ESC后再按F11即可,稍后电脑会重启. 继续安装,则按回车键 按F11同意声明继续 选择将EXSI 安装到哪个硬盘上,我这里使用的是虚拟机,所以只有这一个选项 选择默认键盘布局,默认的美国键盘即可 设置root…

每日一题---OJ题: 有效的括号

片头 嗨! 小伙伴们,大家好! 我们又见面啦! 今天我们来一起尝试一下这道题目---有效的括号,准备好了吗? 我们开始咯! 说实话,我刚开始做这道题的时候也是一脸懵,怎么进行括号匹配呢? 别慌,我们一起画个图,分析分析括号匹配的过程~ 如下图所示,上方表示一个字符串数组,存放不…

hadoop最新详细版安装教程 2024 最新版

文章目录 hadoop安装教程 2024最新版提前准备工作用户配置安装 SSH Server免密登录设置编辑 SSH server 配置文件配置Java环境查看java 版本验证 环境变量设置安装Hadoop下载hadoop解压hadoop查看hadoop 版本hadoop 配置编辑编辑配置文件core-site.xml编辑配置文件hdfs-site.xm…

gma 2 用户文档(pdf版)更新计划

随着 gma 2 整体构建完成&#xff0c;下一步继续针对库内所有功能完成一个用户指南&#xff08;非网站&#xff09;。相较于上次更新用户文档pdf版&#xff0c;已经过去了大半年。当然&#xff0c;PDF 版比网站上内容更丰富&#xff0c;也更新&#xff08;文档基于 gma 2.0.9a2…

Spring Cloud Gateway详细介绍以及实现动态路由

一. 简介 Spring Cloud Gateway This project provides a libraries for building an API Gateway on top of Spring WebFlux or Spring WebMVC. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to …

【读书笔记】自动驾驶与机器人中的SLAM技术——高翔

文章会对本书第五章节及以后章节进行总结概括。每日更新一部分。一起读书吧。 第五章——基础点云处理 重点&#xff1a;点云的相邻关系是许多算法的基础 5.1 激光雷达传感器与点云的数学模型 5.1.1激光雷达传感器的数学模型 雷达有两种&#xff1a;机械旋转式激光雷达&…

018——红外遥控模块驱动开发(基于HS0038和I.MX6uLL)

目录 一、 模块介绍 1.1 简介 1.2 协议 二、 驱动代码 三、 应用代码 四、 实验 五、 程序优化 一、 模块介绍 1.1 简介 红外遥控被广泛应用于家用电器、工业控制和智能仪器系统中&#xff0c;像我们熟知的有电视机盒子遥控器、空调遥控器。红外遥控器系统分为发送端和…

RISCV指令集体系简读之RV32M

RV32M向RV32I中添加了整数乘法和除法指令&#xff1b; RV32M具有有符号和无符号整数的除法指令&#xff1a;divide(div)和divide unsigned(divu)&#xff0c;它们将 商放入目标寄存器。在少数情况下&#xff0c;程序员需要余数而不是商&#xff0c;因此RV32M提供 remainder(rem…

46.HarmonyOS鸿蒙系统 App(ArkUI)网格布局

Grid(){GridItem(){Button(按钮1).fontSize(28)}.backgroundColor(Color.Blue)GridItem(){Text(数学).fontSize(28)}.backgroundColor(Color.Yellow)GridItem(){Text(语文).fontSize(28)}.backgroundColor(Color.Green)GridItem(){Text(英语).fontSize(28)}.backgroundColor(Co…

结构型模式--3.组合模式【草帽大船团】

1. 好大一棵树 路飞在德雷斯罗萨打败多弗朗明哥之后&#xff0c;一些被路飞解救的海贼团自愿加入路飞麾下&#xff0c;自此组成了草帽大船团&#xff0c;旗下有7为船长&#xff0c;分别是&#xff1a; 俊美海贼团75人 巴托俱乐部56人 八宝水军1000人 艾迪欧海贼团4人 咚塔塔海…

暴雨信息专场推介会亮相武汉国际创科展

4月14日&#xff0c;暴雨信息专场推介会亮相2024年武汉国际创科展。推介会以“智慧聚变新生”为主题&#xff0c;集中展示推介暴雨信息旗下多项颇具市场优势的智能化产品和解决方案&#xff0c;邀请多位专家分享在数字化转型领域的成果和实践经验&#xff0c;与业界同仁共同探讨…

osg渲染过程

目录 1、渲染最简单代码 2、详解run方法 3、详细过程 4、回调函数 5、Node Visitor 1、渲染最简单代码 2、详解run方法 3、详细过程 3.1 advance()方法 进行帧计数 3.2 eventTraversal() eventTraversal()响应用户操作,eventTraversal()遍历的是事件队列&#xff0c;而…

Java开发从入门到精通(二十):Java的面向对象编程OOP:Collection集合框架

Java大数据开发和安全开发 &#xff08;一&#xff09;Java的集合进阶1.1 集合体系结构1.2 Collection集合1.2.1 Collection集合包含哪些接口和实现类1.2.2 Collection集合特点1.2.3 为啥要先学Collection的常用方法?1.2.4 Collection集合的遍历1.2.4.1 迭代器1.2.4.1.1 迭代器…

基于SpringBoot+Vue实现的医院在线挂号系统(代码+万字文档)

系统介绍 基于SpringBootVue实现的医院在线挂号系统设计了三种角色&#xff0c;分别是管理员、医生、用户&#xff0c;每种角色对应不同的菜单 系统实现了个人信息管理、基础数据管理、论坛管理、用户管理、单页数据管理、医生管理及轮播图管理等功能模块&#xff0c;具体功能…

16.读取指定路径下的txt文档然后合并内容为一个txt文档。

1.题目要求 分别读取路径为 ./middle/phone/base/1_student_0.txt, ./middle/vr/base/1_teacher.txt, ./nearby/phone/base/1_student_0.txt, ./nearby/vr/base/1_teacher.txt, ./outside/phone/base/1_student_0.txt, ./outside/vr/base/1_teacher.txt 里面的文件&#xff…

【LeetCode: 3117. 划分数组得到最小的值之和 + 动态规划】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

linux shell脚本编写(2)

Shell: 命令转换器&#xff0c;高级语言转换成二进制语言。是Linux的一个外壳&#xff0c;它包在Lniux内核的外面&#xff0c;用户和内核之间的交互提供了一个接口。 内置命令&#xff1a;在shell内部不需要shell编辑 外置命令&#xff1a;高级语言要用shell转换成二进制语言 …

Numpy数组和列表list的区别

参考&#xff1a;Numpy Array vs List 在Python编程中&#xff0c;列表&#xff08;list&#xff09;和Numpy数组&#xff08;numpy array&#xff09;是两种常见的数据结构&#xff0c;它们都可以用来存储多个元素。但是它们在实际使用中有很大的区别&#xff0c;本文将详细比…

【尚硅谷】Git与GitLab的企业实战 学习笔记

目录 第1章 Git概述 1. 何为版本控制 2. 为什么需要版本控制 3. 版本控制工具 4. Git简史 5. Git工作机制 6. Git和代码托管中心 第2章 Git安装 第3章 Git常用命令 1. 设置用户签名 1.1 基本语法 1.2 案例实操 2. 初始化本地库 2.1 基本语法 2.2 案例实操 3. 查…

RabbbitMQ基本使用及其五种工作模型

初识MQ 同步通讯和异步通讯 什么是同步通讯呢&#xff1f;举个例子&#xff0c;你认识了一个小姐姐&#xff0c;聊的很火热&#xff0c;于是你们慢慢开始打电话&#xff0c;视频聊天&#xff0c;这种方式就成为同步通讯&#xff0c;那什么是一部通讯呢&#xff0c;同样的&…