一、互斥锁

  为啥要有互斥?

  多个进程/线程执行的先后顺序不确定,何时切出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线程互斥学习笔记--详细分析的更多相关文章

  1. Linux下iptables学习笔记

    Linux下iptables学习笔记 在Centos7版本之后,防火墙应用已经由从前的iptables转变为firewall这款应用了.但是,当今绝大多数的Linux版本(特别是企业中)还是使用的6. ...

  2. Linux内核OOM机制的详细分析(转)

    Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...

  3. Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)

    Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)

  4. Linux进程间通信IPC学习笔记之同步二(Posix 信号量)

    Linux进程间通信IPC学习笔记之同步二(Posix 信号量)

  5. Linux进程间通信IPC学习笔记之消息队列(SVR4)

    Linux进程间通信IPC学习笔记之消息队列(SVR4)

  6. Linux防火墙iptables学习笔记(三)iptables命令详解和举例[转载]

     Linux防火墙iptables学习笔记(三)iptables命令详解和举例 2008-10-16 23:45:46 转载 网上看到这个配置讲解得还比较易懂,就转过来了,大家一起看下,希望对您工作能 ...

  7. linux基础命令学习笔记(二)

    linux基础命令学习笔记(二) 1.kill :终止进程  kill pid (唯一标示一个进程) kill -9  强制终止  kill -15 命令未结束不能终止 # ps aux 查看所有进程 ...

  8. 兄弟连Linux运维学习笔记

    最新经典linux运维兄弟连Linux运维学习笔记... --------------- 全程1.5倍播放.加油我一定可以学完Linux----------------------Unix与Linux ...

  9. Linux shell 菜鸟学习笔记....

    20171123 Linux shell 基础学习笔记1. shell 的开始 一般是 #!/bin/bash 通过 #! 来唯一指定使用的shell路径 其他的 # 都表示注释.2. shell 的 ...

随机推荐

  1. [转]SpringBoot系列——花里胡哨的banner.txt

    Creating ASCII Text Banners from the Linux Command Line In Ubuntu, Debian, Linux Mint etc. $ sudo ap ...

  2. C# 动态构建表达式树(二)——构建 Select 和 GroupBy 的表达式

    C# 动态构建表达式树(二)--构建 Select 和 GroupBy 的表达式 前言 在上篇中写了表达式的基本使用,为 Where 方法动态构建了表达式.在这篇中会写如何为 Select 和 Gro ...

  3. CTFd+ubuntu service搭建等待更新

    CTFd是一款基于Apache2.0的协议的开源CTF平台,最新版本目前为1.20.该平台功能强大,基本上能够满足目前的CTF竞赛需求,同时,该平台提供了强大的插件功能,可以自己进行插件开发实现自己的 ...

  4. windows 下使用 mingw编译器 调试时 无法跟进源码

    windows 下使用 mingw编译器 调试时 无法跟进源码 最近在公司使用QT 开发,官方在线下载的 安装的QT mingw 都是没有debug版本的 由于没有debug版本动态库 所以你调试的时 ...

  5. webpack learn1-初始化项目1

    使用Visual  Studio Code软件使用准备,先安装一些插件,加快开发效率(还有Language Packs 选择简体中文安装后重启软件,可切换为中文): 下面是项目初始化步骤: 1 软件打 ...

  6. jmeter5.2 性能测试 资源监控 JMeterPlugins1.4 ServerAgent2.2.1

    一.性能工具的安装部署 1.下载JMeterPlugins-Standard-1.4.0.zip的安装包 2.解压JMeterPlugins-Standard-1.4.0.zip,将其中\lib\ex ...

  7. c++ 的学习笔记 第一集cim cout

    1. 你要用这个东西,所以得有包含它得头文件,就像java 你要用某个模块,你得包含这个模块 模块化??单片机里面学的模块化(可以在vs里面实现) 2. 当我把注册表regedit 程序删除之后成功了 ...

  8. 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 百篇博客分析OpenHarmony源码 | v7.07

    百篇博客系列篇.本篇为: v07.xx 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  9. Python第3方模块安装

    前言: 我们在实际开发过程中,很多模块是Python没有自带的,所以我们使用模块前需要先安装第三方模块. 在线安装: 1.为了简便cmd指令操作,建议先打开Python目录下的Scripts文件夹下, ...

  10. kubelet源码分析——启动Pod

    前文说到Kubelet启动时,调用到kubelet.Run方法,里面最核心的就是调用到kubelet.syncLoop.它是一个循环,这个循环里面有若干个检查和同步操作,其中一个是地在监听Pod的增删 ...