Linux线程互斥学习笔记--详细分析
一、互斥锁
为啥要有互斥?
多个进程/线程执行的先后顺序不确定,何时切出CPU也不确定。
多个进程/线程访问变量的动作往往不是原子的。
1. 操作步骤
(1)创建锁
// 创建互斥锁mutex pthread_mutex_t mutex;
(2)初始化锁
在Linux下, 线程的互斥量数据类型是pthread_mutex_t 在使用前, 要对它进行初始化:
初始化的两种方法:(推荐使用第二种)
1.静态分配
pthread_mutex mutex = PTHREAD_MUTEX_INITIALIZER;
2.动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
mutex: 要初始化的互斥量(restrict的作用是告诉调用者,不要改变指针的指向)
attr:锁的属性,一般写NULL
加restrict的作用:只用于修饰函数参数里的指针,这个指针会频繁使用,所以把这个地址放到寄存器里,用着好找。
①设置线程的属性
int pthread_attr_init(pthread_attr_t *attr);//初始化线程属性 int pthread_attr_destroy(pthread_attr_t *attr);//销毁线程属性
Thread attributes(线程属性):
线程的分离属性: Detach state=PTHREAD_CREATE_DETACHED
线程的竞争范围: Scope = PTHREAD_SCOPE_SYSTEM
是否继承调度策略: Inherit scheduler = PTHREAD_EXPLICIT_SCHED
调度策略: Scheduling policy = SCHED_OTHER
调度优先级: Scheduling priority = 0
线程栈之间的保留区域: Guard size = 4096 bytes
自己指定栈地址: Stack address = 0x40197000
栈大小: Stack size = 0x3000000 bytes
//设置线程的分离属性 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); //detachstate:有以下两种选择 PTHREAD_CREATE_DETACHED:设置成分离态 PTHREAD_CREATE_JOINABLE:设置成可结合态 //获取线程的分离属性 int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); //int *detachstate:输出型参数,将分离属性存放在该变量里
(3)上锁 && 解锁
对共享资源的访问, 要对互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁. 在完成了对共享资源的访问后,要对互斥量进行解锁。
具体说一下trylock函数, 这个函数是非阻塞调用模式, 也就是说, 如果互斥量没被锁住, trylock函数将把互斥量加锁, 并获得对共享资源的访问权限; 如果互斥量被锁住了, trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态。
锁粒度(尽量小):临界区操作个数,执行时间的长短
int pthread_mutex_lock(pthread_mutex_t *mutex); //加锁:如果是1,置0,返回 // 如果是0,阻塞 int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞加锁 int pthread_mutex_unlock(pthread_mutex_t *mutex); //解锁
返回值: 成功则返回0, 出错则返回错误编号.
(4)销毁互斥锁
对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化, 并且在释放内存(free)前需要调用pthread_mutex_destroy.
注意:
使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁
不要销毁一个已经加锁的互斥量
已销毁的互斥量要确保后面不会有线程尝试加锁
pthread_mutex_destroy(&lock); //销毁
返回值: 成功则返回0, 出错则返回错误编号.
说明: 如果使用默认的属性初始化互斥量, 只需把attr设为NULL. 其他值在以后讲解。
2. 死锁
(1)死锁的两种情况:
情况1:
如果两个线程先后调用两次lock,第二次调用lock时,由于锁已被占用,该线程会挂起等待别的线程释放锁,然后锁正是被自己占用着的,该线程又被挂起不能释放锁,因此就永远处于挂起等待状态了,这就叫死锁。
情况2:
有线程A、B。A获得锁1,B获得锁2,此时A调用lock企图获得锁2,结果是需要挂起等待B释放锁2,而此时B也调用了lock企图获得锁1,结果是B挂起等待A释放锁1,于是乎A、B永远处于挂起状态。
(2)避免的死锁的原则
死锁主要发生在有多个依赖锁存在时,会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生.如何避免死锁是使用互斥量应该格外注意的东西。
总体来讲, 有几个不成文的基本原则:
对共享资源操作前一定要获得锁。
完成操作以后一定要释放锁。
尽量短时间地占用锁。
如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。
线程错误返回时应该释放它所获得的锁。
写程序是尽量避免同时获得多个锁,如果一定要这么做,则遵循一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见是按mutex变量的地址顺序)获得锁,则不会出现死锁。
mutex互斥信号量锁住的不是一个变量,而是阻塞住一段程序。如果对一个mutex变量testlock,执行了第一次pthread_mutex_lock(testlock)之后,在unlock之前的这段时间内,如果有其他线程也执行到了pthread_mutex_lock,这个线程就会阻塞住,直到之前的线程unlock之后才能执行,由此,实现同步,也就达到保护临界区资源的目的。
为了实现互斥操作,大多数体系结构提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据交换,由于只有一条指令,保证了原子性。即使是多处理器平台,访问内存的总线周期也有先后,一个处理器的交换指令执行时另一个处理器的交换指令只能等待总线周期。
(3)临界区代码原则
短——临界区代码简洁明了;
平——临界区代码逻辑清晰,没有复杂的函数调用尤其是尽量不要申请其他互斥资源;
快:临界区代码执行速度快。
3. 互斥锁和信号量的区别
互斥量用于线程的互斥,信号线用于线程的同步。
这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源
互斥量值只能为0/1,信号量值可以为非负整数。
也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。
互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。
4. 线程安全和可重入
可重入函数:在多个执行流中被同时调用不会存在问题。
线程安全函数:在多线程中被同时调用不会存在问题。
可重入函数一般情况下都是线程安全的
线程安全函数不一定是可重入函数
二、自旋锁
1. 操作步骤
//1. 定义自旋锁 pthread_spinlock_t spin; //2. 初始化自旋锁 int pthread_spin_init(pthread_spinlock_t *lock, int pshared); //3. 上锁 int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); //4. 解锁 int pthread_spin_unlock(pthread_spinlock_t *lock); //5. 销毁锁 int pthread_spin_destroy(pthread_spinlock_t *lock);
2. 自旋锁和互斥锁的区别
互斥锁是当阻塞在pthread_mutex_lock时,放弃CPU,好让别人使用CPU。自旋锁阻塞在pthread_spin_lock时,不会释放CPU,不断向cup询问可以用了不。
三、读写锁
1. 读写锁的规则
读读共享
读写排他
写写排他
写优先级高
2. 操作步骤
// 1. 定义锁 pthread_rwlock_t lock; // 2. 初始化 pthread_rwlock_init(&lock, NULL); // 3. 读锁 pthread_rwlock_rdlock(&lock); pthread_rwlock_wrlock(&lock); // 4. 解锁 pthread_rwlock_unlock(&lock); // 5. 销毁锁 pthread_rwlock_destroy(&lock);
3. 代码实现
#include #include #include #include int g_count; pthread_rwlock_t rw; void* route_read(void* arg) { int id = *(int*)arg; free(arg); while(1) { pthread_rwlock_rdlock(&rw); printf("%d read_pthread : %d\n", id, g_count); pthread_rwlock_unlock(&rw); usleep(100000); } } void* route_write(void* arg) { int id = *(int*)arg; free(arg); while(1) { pthread_rwlock_wrlock(&rw); printf("%d write_pthread : %d\n", id, ++g_count); pthread_rwlock_unlock(&rw); usleep(100000); } } int main() { pthread_t w1, r1, r2, r3; pthread_rwlock_init(&rw, NULL); int* p = (int*)malloc(sizeof(int)); *p = 1; pthread_create(&w1, NULL, route_write, p); int* p1 = (int*)malloc(sizeof(int)); *p1 = 1; pthread_create(&r1, NULL, route_read, p1); int* p2 = (int*)malloc(sizeof(int)); *p2 = 2; pthread_create(&r2, NULL, route_read, p2); int* p3 = (int*)malloc(sizeof(int)); *p3 = 3; pthread_create(&r3, NULL, route_read, p3); pthread_join(w1, NULL); pthread_join(r1, NULL); pthread_join(r2, NULL); pthread_join(r3, NULL); pthread_rwlock_destroy(&rw); }
、Linux线程互斥的视频资料
http://www.makeru.com.cn/live/1392_715.html?s=45051
可以加vx获取更多有用的Linux学习资料
Linux线程互斥学习笔记--详细分析的更多相关文章
- Linux下iptables学习笔记
Linux下iptables学习笔记 在Centos7版本之后,防火墙应用已经由从前的iptables转变为firewall这款应用了.但是,当今绝大多数的Linux版本(特别是企业中)还是使用的6. ...
- Linux内核OOM机制的详细分析(转)
Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...
- Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)
Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)
- Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
- Linux进程间通信IPC学习笔记之消息队列(SVR4)
Linux进程间通信IPC学习笔记之消息队列(SVR4)
- Linux防火墙iptables学习笔记(三)iptables命令详解和举例[转载]
Linux防火墙iptables学习笔记(三)iptables命令详解和举例 2008-10-16 23:45:46 转载 网上看到这个配置讲解得还比较易懂,就转过来了,大家一起看下,希望对您工作能 ...
- linux基础命令学习笔记(二)
linux基础命令学习笔记(二) 1.kill :终止进程 kill pid (唯一标示一个进程) kill -9 强制终止 kill -15 命令未结束不能终止 # ps aux 查看所有进程 ...
- 兄弟连Linux运维学习笔记
最新经典linux运维兄弟连Linux运维学习笔记... --------------- 全程1.5倍播放.加油我一定可以学完Linux----------------------Unix与Linux ...
- Linux shell 菜鸟学习笔记....
20171123 Linux shell 基础学习笔记1. shell 的开始 一般是 #!/bin/bash 通过 #! 来唯一指定使用的shell路径 其他的 # 都表示注释.2. shell 的 ...
随机推荐
- Dockerfile常见命令
Dockerfile结构 Dockerfile的结构分成了若干部分,每个部分之间的先后顺序有明确的要求: 部分 命令 基础镜像信息 FROM 维护者信息 MAINTAINER 镜像操作指令 RUN.C ...
- 搭建http文件服务器 - python3使用http.server搭建http文件服务器
适用场景 只要有python3就可以,windows系统cmd窗口直接敲命令,Linux系统,直接敲命令 step-1 cd cd 到需要搭建服务器的目录 step-2 http.server pyt ...
- Jmeter系列(20)- 录制控制器
作用:相当于对录制的脚本进行分组存放,放在同一个线程组里面:录制脚本的时候,选择线程组下面想要存放的录制控制器中
- python 语法规范
在python shell 中输入 import this 可以看到python之禅 The Zen of Python, by Tim Peters Beautiful is better than ...
- android web外壳
参考: 1.https://blog.csdn.net/m0_37201243/article/details/106862817 2.https://www.cnblogs.com/ifaswind ...
- 《DotNet Web应用单文件部署系列》三、混淆dll文件
众所周知,C#编译后的dll文件可被反编译,网上搜索"C# 反编译"会出现一大堆资料.为了提高反编译成本,我们必须对dll文件进行混淆处理. 目前,C#混淆工具很多,我推荐obfu ...
- P5494-[模板]线段树分裂
正题 题目链接:https://www.luogu.com.cn/problem/P5494 题目大意 给出一个可重集合要求支持 将集合\(p\)中在\([l,r]\)的数放到一个新的集合中 将集合\ ...
- springweb项目自定义拦截器修改请求报文头
面向切面,法力无边,任何脏活累活,都可以从干干净净整齐划一的业务代码中抽出来,无非就是加一层,项目里两个步骤间可以被分层的设计渗透成筛子. 举个例子: 最近我们对接某银行接口,我们的web服务都是标准 ...
- 人力节省 50%,研发效能提升 40%,阿里 Serverless 架构落地实践
作者 | 万佳 嘉宾 | 杨皓然(不瞋) 导读:云的下一波浪潮是什么?杨皓然称"是 Serverless".作为一名阿里老兵,他早在 2010 年即加入阿里云,曾深度参与阿里云飞天 ...
- Unity——资源文件夹介绍
Unity资源文件夹介绍 1.编辑时 在Asset文件下存在Resources和SteamingAsset文件夹: Resources 只读不可修改,打包时直接写死,没有办法通过热更新替换资源: 可以 ...