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 的 ...
随机推荐
- C++ windows 函数讲解(二)鼠标坐标
获得鼠标坐标: #include<bits/stdc++.h> #include<windows.h> using namespace std; int main() { PO ...
- STM32CbueIDE 体验
STM32CbueIDE 体验 下载安装 官网下载链接:https://www.st.com/zh/development-tools/stm32cubeide.html. 软件启动时会令设置工作目录 ...
- 【PHP数据结构】图的应用:最短路径
上篇文章的最小生成树有没有意犹未尽的感觉呀?不知道大家掌握得怎么样,是不是搞清楚了普里姆和克鲁斯卡尔这两种算法的原理了呢?面试的时候如果你写不出,至少得说出个大概来吧,当然,如果你是要考研的学生,那就 ...
- PHP中的那些魔术常量
之前我们已经了解了一些常用的魔术方法,除了魔术方法外,PHP还提供一些魔术常量,相信大家在日常的工作中也都使用过,这里给大家做一个总结. 其实PHP还提供了很多常量但都依赖于各类扩展库,而有几个常量是 ...
- 【PHP】随机生成名字
public function test(){ $a = "赵 钱 孙 李 周 吴 郑 王 冯 陈 楮 卫 蒋 沈 韩 杨 朱 秦 尤 许 何 吕 施 张 孔 曹 严 华 金 魏 陶 姜 戚 ...
- 一朵云、一张网、一体化 ——GRTN 打造最佳流媒体场景实践
阿里巴巴 GRTN 是面向流媒体云原生设计的,方便客户构建自己的流媒体云原生应用,让流媒体服务无处不在. 在近期召开的分布式云主题报告会上,阿里云资深技术专家卢日发表了题为<GRTN 打造阿里云 ...
- win10系统移动热点使用技巧
win10系统是自动移动热点功能,在平时测试的时候,有时需要进行手机抓包,需要手机和电脑处于同一网络当中,这时可以开启热点使用. 如何开启移动热点? 直接搜索"移动热点" 但是如果 ...
- Mybatis-Plus 全局Update更新策略,和insert插入查询策略
前言 最近在使用mybatis-plus做项目的时候,发现使用updatById方法的时候,更新某个字段时候出现了问题,一般业务操作都是更新不为空的字段,结果发现更新了所有字段,这是由于mybatis ...
- P4780-Phi的反函数【dfs】
正题 题目链接:https://www.luogu.com.cn/problem/P4780 题目大意 给出\(n\),求一个最小的\(x\)满足\(\varphi(x)=n\). 若不存在或者大于\ ...
- P3964-[TJOI2013]松鼠聚会【计算几何】
正题 题目链接:https://www.luogu.com.cn/problem/P3964 题目大意 给出\(n\)个点,求一个点使得它到所有点的切比雪夫距离和最小. \(0\leq n\leq 1 ...