hello 你好呀!,我是aoz,今天我们来一起研究Linux互斥锁

废话不多说,我们进入正题 有需要的同学可以自行跳转哟~

文章目录

文章目录

  • 文章目录
  • 0.线程基础概念
  • 1. 创建和销毁
    • 1.1静态初始化方法及要点
    • 1.2 动态初始化方法及要点
    • 1.3 销毁锁
  • 2.锁定与解锁
  • 3.死锁的产生及解决

0.线程基础概念

想要理解互斥锁就得先理解线程的概念与临界区的概念呦~

打个比方,线程就像两个人在聊天,分为悄悄话和公屏聊天
悄悄话只能自己听见,但是公屏聊天大家都能听见,因为共用一个公屏,所以还得抢

悄悄话 = 线程内操作,线程外拿不到的数据
公屏聊天 = 临界区操作,共用区域

为了方便理解线程和互斥锁的概念我画了一个图,下面了两个线程,线程对同一个数据区域进行访问

那什么时候用到互斥锁呢?

当你说话不想被打断的时候。

你以为的多线程运行逻辑

实际的多线程运行逻辑

多线程的操作并不是在系统中真的有两个线程在跑,而是一个线程执行一段时间再切到另一个线程上去,这种线程切换非常快,如果进行一个非常长的写入操作,可能写到了一半线程就切走了,那么就会出现写入不完全,也就是话说了一半没说完,这样的话被别的线程听去,就会出大乱子,因此在任一线程说话的时候,就需要让另一个线程闭嘴,不能打断他。

在Posix Thread中定义有一套专门用于线程同步的mutex函数,专门用于锁定某些线程对于临界区的操作。

具体是这样的

运行…
加锁 lock
临界区长写入操作
很长
很长
长…
解锁 unlock

1. 创建和销毁

有两种方法创建互斥锁, 静态方式动态方式

先说结果:
动态初始化在堆中创建,不用了需要删除以释放内存;
静态初始化在静态存储区,初始化之后直接用就可以,不用了也不需要删除。

1.1静态初始化方法及要点

静态初始化其实是使用了一个POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER

下面这段转载,戳这里转原文

#include <pthread.h>
int main(int argc,char *argv[])
{ pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; /*正确的使用方法*/pthread_mutex_t mutex2; /* 错误 */mutex2 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex3; /* 错误 */mutex3 = mutex1; /* 这里注意, mutex3的赋值是成功的。*/return 0;
}

上面三个例子,其中mutex1 是正确初始化的,mutex2 直接编译报错,mutex3 编译不报错,但是这种赋值的结果是未定的,所以应该禁止这种用法。

编译错误如下:

mingq@mingq-linux:~/test$ g++ test8.cpp -o test8 -lpthread
test8.cpp: In function ‘int main(int, char**)’:
test8.cpp:12:14: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11mutex2 = PTHREAD_MUTEX_INITIALIZER; ^

原因分析

静态初始化过程就是编译器在编译的过程中完成了某些内存空间的初始化,也就是说这个**初始化过程发生在编译时,而不是运行时,**因此称之为静态初始化。

PTHREAD_MUTEX_INITIALIZER 的完整定义为:

# define PTHREAD_MUTEX_INITIALIZER \ { { 0, 0, 0, 0, 0, 0, { 0, 0 } } }

上述问题并不仅仅针对于Mutex变量,而是所有的结构体变量。结构体变量在使用常量进行整体初始化的时候只能在声明定义的时候进行,不能是声明结束之后。

1.2 动态初始化方法及要点

动态方式是采用pthread_mutex_init()函数进行初始化,

API定义如下: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性

互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性(缺省值),不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。 当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

属性 名称 说明 解锁者
PTHREAD_MUTEX_TIMED_NP 缺省值 就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。 同进程内任何线程
PTHREAD_MUTEX_RECURSIVE_NP 嵌套锁 允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。 加锁者(暂定)
PTHREAD_MUTEX_ERRORCHECK_NP 检错锁 如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。 加锁者
PTHREAD_MUTEX_ADAPTIVE_NP 适应锁 动作最简单的锁类型,仅等待解锁后重新竞争 同进程内任何线程
#include <pthread.h>
int main(int argc, char *argv[])
{pthread_mutex_t *pMutex = NULL;pMutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); /* 需要申请内存 */pthread_mutex_init(pMutex, NULL);pthread_mutex_destroy(pMutex); /* 同时程序或者模块结束时互斥锁要记得销毁,不然是资源泄漏。*/free(pMutex);                  /*销毁之后要记得释放内存,不然会内存泄漏。 */
}

1.3 销毁锁

pthread_mutex_destroy ()用于注销一个互斥锁
API定义如下:int pthread_mutex_destroy(pthread_mutex_t *mutex)销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态

由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy( )除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

2.锁定与解锁

锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个
无论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁

对于普通锁适应锁类型,解锁者可以是同进程内任何线程;

检错锁则必须由加锁者解锁才有效,否则返回EPERM

对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同还没有得到解释。

在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

int pthread_mutex_lock(pthread_mutex_t *mutex)int pthread_mutex_unlock(pthread_mutex_t *mutex)int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

注意

加锁之后,未解锁之前,其他线程请求加锁时,会进入阻塞状态,直到这边解锁为止

任何地方加锁,在返回的时候的要记得解锁,不然进程就死锁了

原则上,谁加锁,谁就来解锁

3.死锁的产生及解决

1.任何地方线程加锁,在返回的时候的要记得解锁,不然进程中如果其他线程申请加锁的时候就永远阻塞了,这就造成了死锁了

2.两个锁以上的情况。以不同的线程以相反的顺序加锁,很容易造成死锁

3.同个线程在加锁成功后,未解锁的情况下,重新申请加锁

Linux互斥锁详细解读,看这一篇就够了相关推荐

  1. Linux基础知识汇总,看这一篇就够了(2022最新整理)

    前言 想入门Llinux,新手不知道Linux怎么入门?有这一篇就够了 提示:以下是本篇文章正文内容,下面案例可供参考 一.Linux入门概述 概述 Linux全称GNU/Linux,是一种免费使用和 ...

  2. Linux_进程管理详解《鸟哥的Linux私房菜》学习笔记(极其详细,看完这篇就够了)

    前言 当一个程序被载入到内存中运行,那么在内存中的那个程序就被称为进程(process).进程是操作系统上非常重要的概念, 所有系统上面跑的数据都会以进程的形态存在. 那么系统的进程有哪些状态?不同的 ...

  3. Linux 系统基础 — 用户和组(吐血总结,超详细,看这一篇就够了!dog)

    Linux 用户和组 Linux中用户和组的相关文件 /etc/passwd /etc/shadow /etc/group Linux用户 Linux中的组 用户操作 useradd的更多使用方式 p ...

  4. Linux命令大全总结(看这一篇就够了)

    Linux命令大全总结 1.操作系统基础知识 1.1.操作系统     在了解linux命令之前,我们先了解一点基本概念. 1.1.1.概念 操作系统(operating system 简称OS)是一 ...

  5. Linux 系统结构详解,看这一篇就够了

    点击▲关注 "程序IT圈"   给公众号标星置顶 更多精彩 第一时间直达 作者:huangguisu 链接:https://dwz.cn/Jsc4V4Sz Linux系统一般有4个 ...

  6. Linux 系统结构详解,看这一篇就够了?(又一篇万字长文)

    点击上方 "程序员小乐"关注, 星标或置顶一起成长 每天凌晨00点00分, 第一时间与你相约 每日英文 When one reaches a point of difficulty ...

  7. Linux脚本:Bash脚本看这一篇就够了

    前言 Linux脚本有很多解析器(Shell),不同解析器要求的脚本语法是不一样的.系统在解析脚本时,如果没有在脚本声明指定解析器,则会采用系统默认解析器来对脚本进行解析.sh是非常重要解析器,历史很 ...

  8. 【C/C++】内存对齐(超详细,看这一篇就够了)

    目录 一.为什么要内存对齐 二.基本变量类型所占大小 三.影响内存对齐的情况 四.先说结论(重要) 五.举亿点例子(以下内容均实际运行过,质量保证) 例1:研究结构体最后一个成员内存对齐问题1 例2: ...

  9. 《Linux驱动:I2C驱动看这一篇就够了》

    文章目录 一,前言 二,IIC驱动的体系架构 2.1 IIC核心 2.2 IIC适配器 2.2.1 适配器驱动资源的初始化和注册 2.2.2 IIC适配器里的通信方法 2.2.3 IIC适配器和IIC ...

  10. Windows系统 电脑系统重装详细图文教程(绝对够详细,看这一篇就够)

    电脑装机教程    在装机之前,请备份好自己的宝贵的数据资料,一般装机会装在C盘,C盘在装机的时候会格式化,请注意自己的数据资料是否保存了!!    安装前准备 1.U盘一个(尽量使用8G以上的U盘) ...

最新文章

  1. oracle并行收集统计信息慢,Oracle 学习之性能优化(四)收集统计信息
  2. 小组是什么意思_LGD携手UOL晋级S10正赛 10月3日小组赛正式开战!
  3. 微信公众嵌套页面里再嵌入其他页面的一些问题
  4. nyoj116士兵杀死(两)段树单点更新
  5. Comments on The Ph.D. grind------by Yishi
  6. false true求或_如何依据【关键字】求【数量和】
  7. dubbo项目引用另一个项目的接口
  8. c语言中英互译程序,c语言怎么翻译? 程序怎么运行?
  9. python经典程序实例-你不知道的Python语言的经典五大案例
  10. UE4之ACharacter移动人物并显示动画效果
  11. 将Excel中的数据导入至sqlserver数据表
  12. a1277以太网适配器驱动_福禄克DSX系列工业以太网连接器集锦
  13. sqoop的job工具
  14. JQuery文档分析1-JQuery核心与选择器
  15. 鸿蒙系统下载地址_华为鸿蒙代码全开源
  16. 和秋叶一起学Excel 阿里云盘
  17. SWUST OJ 1132: Coin-collecting by robot
  18. 服务器win10系统开机慢,三种方法教你解决Win10系统开机慢,爱纯净官网
  19. 大数据和云计算技术周报(第7期)
  20. 利用手机作为渗透工具的一些思路

热门文章

  1. 30岁哥大计算机博士生遇刺身亡,论文刚被顶会接收,曾留学中国
  2. 【kali】一款黑客们都在使用的操作系统
  3. iOS-边框图片,头像边框
  4. Linux 修改hosts文件
  5. 机器学习笔记(十六):多项式回归、拟合程度、模型泛化
  6. 艾米丽的蜜月旅行Android,美味餐厅:艾米丽的蜜月旅行完整版
  7. 避坑指南——海外服务器租用的这些坑,你踩过吗?
  8. 微信小游戏排行榜:主域和子域
  9. 一个小白的自渡-Git 仓库基础操作
  10. font-spider压缩web font字体