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 的 ...
随机推荐
- (2)java Spring Cloud+Spring boot+mybatis企业快速开发架构之SpringCloud-Spring Cloud是什么?Spring Cloud版本介绍
Spring Cloud 是一系列框架的有序集合.它利用 Spring Boot 的开发便利性,巧妙地简化了分布式系统基础设施的开发,如服务注册.服务发现.配置中心.消息总线.负载均衡.断路器.数 ...
- 【第七篇】- Git 分支管理之Spring Cloud直播商城 b2b2c电子商务技术总结
Git 分支管理 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作. 有人把 Git 的分支模型称为必杀技特性,而正是因为它,将 ...
- 288 day05_异常,线程
day05 [异常.线程] 主要内容 异常.线程 教学目标 [ ] 能够辨别程序中异常和错误的区别 [ ] 说出异常的分类 [ ] 说出虚拟机处理异常的方式 [ ] 列举出常见的三个运行期异常 [ ] ...
- 使用Java MVC模式设计一个学生管理系统
最近在做web实验,要求是用jsp+servlet+mysql实现一个学生管理系统,完成对数据库的增删改查. 效果图: 代码: package dao; import java.util.List ...
- scrum项目冲刺_day01总结
摘要:今日完成任务. 1.app基本框架页面正在进行 2.图像识别正在进行 总任务: 一.appUI页面 二.首页功能: 1.图像识别功能 2.语音识别功能 3.垃圾搜索功能 4.相关新闻爬取 三.我 ...
- 【OI】竖式问题分析与解答
题目:找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合.输入数字集合(相邻数字之间没有空格),输出所有竖式.每个竖式前应有编号,之后应有一个空行 ...
- Writing in the Science 01
INTRODUCTION What makes good writing? Good writing communicates an idea clearly and effectively. Goo ...
- P4585-[FJOI2015]火星商店问题【线段树,可持久化Trie】
正题 题目链接:https://www.luogu.com.cn/problem/P4585 题目大意 \(n\)个集合,开始每个集合中有一个数字. 开启新的一天并且往集合\(s\)中插入数字\(v\ ...
- TCP协议基本概念
TCP协议最主要的特点 TCP是面向连接的运输层协议.这就是说,应用程序在使用TCP协议之前,必须要建立TCP连接,且在传输完毕后,还要断开连接. 每一条TCP连接只能有两个端点,每一条TCP连接只能 ...
- DIVIDEMIX: LEARNING WITH NOISY LABELS AS SEMI-SUPERVISED LEARNING
论文阅读: DIVIDEMIX: LEARNING WITH NOISY LABELS AS SEMI-SUPERVISED LEARNING 作者说明 版权声明:本文为博主原创文章,遵循CC 4.0 ...