博客
关于我
Linux-互斥-互斥锁-死锁
阅读量:496 次
发布时间:2019-03-07

本文共 4048 字,大约阅读时间需要 13 分钟。

文章目录

1. 互斥

1.1 互斥锁的原理

  互斥锁的底层是一个互斥量,而互斥量的本质是一个计数器,计数器的取值只有两种,一种是1,一种是0。1表示当前临界资源可以被访问,0表示当前临界资源不可以被访问。

获取/释放互斥锁的逻辑:

  1.调用加锁接口,加锁接口内部判断计数器的值是否为1,如果为1,则能访问,并且如果加锁成功之后,就会将计数器的值从1变为0,如果为0,则不能访问。
  2.调用解锁逻辑,计数器的值从0变成1,表示资源可用。

加锁:

  寄存器和内存当中的计数器的值进行交换,判断寄存器的当中的值是否为1,如果为1,则能够加锁,反之不能。

1.2 互斥锁的接口

初始化互斥锁变量

  互斥锁的类型:pthread_mutex_t

1.2.1 静态初始化

pthread_mutex_t mutex = PTHREAD_MUTRX_INITIALIZER

  这是一个宏定义,本质上包含多个值
在这里插入图片描述
  root用户下执行下面语句可以打开查看

vim /user/include/pthread.hvim /usr/include/bits/pthreadtypes.h

1.2.2 动态初始化

pthread_mutex_init函数

在这里插入图片描述
  int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);
  mutex:该参数为出参,由调用者传递一个互斥锁变量的地址,由pthread_mutex_init这个函数进行初始化
  attr:互斥锁的属性信息,一般设置为NULL,采用默认属性
注意: 动态初始化互斥锁变量的情况需要动态销毁互斥锁(pthread_mutex_destroy),否则就会造成内存泄露。

1.2.3 加锁

  1.阻塞加锁接口:pthread_mutex_lock函数

在这里插入图片描述
  int pthread_mutex_lock(pthread_mutex_t *mutex);
注意:
  如果互斥锁变量当中的计数器的值为1,调用该接口,则加锁成功,该接口调用完毕,函数返回。如果互斥锁变量的那个汇总的计数器的值为0,调用该接口,则调用该接口的执行流阻塞。

  2.非阻塞加锁接口:pthread_mutex_trylock函数

在这里插入图片描述
  int pthread_mutex_trylock(pthread_mutex_t *mutex);
注意:
  不管有没有加锁成功,都会返回,所以,需要对加锁结果进行判断,判断是否加锁,如果加锁成功,则操作临界资源,反之,则需要循环获取互斥锁,直到拿到互斥锁。

  3.带有超时时间的加锁接口:pthread_mutex_timedlock

在这里插入图片描述
   int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);

  超时时间内,如果没有获得互斥锁,则返回,超时时间内,如果获取了互斥锁也直接返回。

注意:
  带有超时时间的加锁接口还需要引入头文件time.h
在这里插入图片描述

1.2.4 解锁接口

  pthread_mutex_unlock函数

在这里插入图片描述
  int pthread_mutex_unlock(pthread_t* mutex)
注意:
  不管是阻塞加锁/非阻塞加锁/timedlock加锁成功都可以使用该接口进行解锁。

1.2.5 销毁接口

  pthread_mutex_destroy

在这里插入图片描述
  int pthread_mutex_destroy(pthread_t* mutex)
功能: 释放动态开辟的互斥锁的资源

1.3 互斥锁的使用

1.3.1 什么时候要初始化互斥锁?

  在创建工作线程之前,进行初始化互斥锁。

1.3.2 什么时候进行加锁?

  在执行流访问临界资源之前进行加锁操作。

注意:
  一个执行流加锁成功之后,再去获取互斥锁,该执行流也会阻塞。加锁之后一定要记得解锁,否则就会导致死锁。

1.3.3 什么时候解锁?

  在执行流所有有可能退出的地方进行解锁。

1.3.4 什么时候释放互斥锁资源?

  在所有使用该互斥锁的线程全部退出之后,就可以释放该互斥锁了。

1.4 代码验证

#include 
#include
#include
#define THREAD_NUM 2int g_tickets = 100000;pthread_mutex_t my_lock;void* MyThreadStart(void* arg){ while(1) { pthread_mutex_lock(&my_lock); if(g_tickets > 0) { printf("i have %d, i am %p\n", g_tickets, pthread_self()); g_tickets--; } else { pthread_mutex_unlock(&my_lock); pthread_exit(NULL); } pthread_mutex_unlock(&my_lock); } return NULL;}int main(){ pthread_mutex_init(&my_lock, NULL); pthread_t tid[THREAD_NUM]; for(int i = 0; i < THREAD_NUM; i++) { int ret = pthread_create(&tid[i], NULL, MyThreadStart, NULL); if(ret < 0) { perror("pthread_create"); return 0; } } for(int i = 0; i < THREAD_NUM; i++) { pthread_join(tid[i], NULL); } pthread_mutex_destroy(&my_lock); printf("phread_join end...\n"); return 0;}

在这里插入图片描述

在这里插入图片描述
  因为我这使用的这个机器只有一个CPU所以,线程只能是并发的执行。

2. 死锁

2.1 死锁的定义

  简单的定义:当一个执行流获取到互斥锁之后,并没有进行解锁,就会导致其他执行流由于获取不到锁资源而进行阻塞,我们将这个现象称之为死锁。

  复杂的定义:当线程A获取到互斥锁1,线程B获取到互斥锁2的时候,线程A和线程B同时还想获取对方手中的锁(线程A还想获取互斥锁2,线程B还想获取互斥锁1),此时就会导致死锁。

2.2 死锁的现象

在这里插入图片描述

2.3 死锁的代码

#include 
#include
#include
pthread_mutex_t lock_1;pthread_mutex_t lock_2;void* MyThread_A(void* arg){ pthread_mutex_lock(&lock_1); sleep(2); pthread_mutex_lock(&lock_2); return NULL;}void* MyThread_B(void* arg){ pthread_mutex_lock(&lock_2); sleep(2); pthread_mutex_lock(&lock_1); return NULL;}int main(){ pthread_mutex_init(&lock_1, NULL); pthread_mutex_init(&lock_2, NULL); pthread_t tid_A, tid_B; pthread_create(&tid_A, NULL, MyThread_A, NULL); pthread_create(&tid_B, NULL, MyThread_B, NULL); pthread_join(tid_A, NULL); pthread_join(tid_A, NULL); pthread_mutex_destroy(&lock_1); pthread_mutex_destroy(&lock_2); return 0;}

1.执行死锁的代码

在这里插入图片描述
2.查看进程号
在这里插入图片描述
3.调试正在运行的进程
在这里插入图片描述
4.查看当前线程(主线程)的调用堆栈
在这里插入图片描述
5.将查看调用堆栈这个功能应用于所有线程
在这里插入图片描述
6.跳转到3号线程
在这里插入图片描述
7.查看锁的情况
在这里插入图片描述

2.4 死锁的必要条件

  1.不可剥夺:执行流获取了互斥锁之后,除了自己主动释放锁,其他执行流不能解释该互斥锁

  2.循环等待
  3.互斥条件:一个互斥锁只能被一个执行流在同一时刻拥有
  4.请求与保持:“吃着碗里瞧着锅里”

2.5 预防死锁

  1.破坏必要条件:循环等待或者请求与保持

  2.加锁顺序一致
  3.避免锁没有被释放
  4.资源一次性分配

2.6 避免死锁的算法

  1.死锁检测算法

  2.银行家算法

转载地址:http://soxcz.baihongyu.com/

你可能感兴趣的文章
MySQL 的全局锁、表锁和行锁
查看>>
mysql 的存储引擎介绍
查看>>
MySQL 的存储引擎有哪些?为什么常用InnoDB?
查看>>
Mysql 知识回顾总结-索引
查看>>
Mysql 笔记
查看>>
MySQL 精选 60 道面试题(含答案)
查看>>
mysql 索引
查看>>
MySQL 索引失效的 15 种场景!
查看>>
MySQL 索引深入解析及优化策略
查看>>
MySQL 索引的面试题总结
查看>>
mysql 索引类型以及创建
查看>>
MySQL 索引连环问题,你能答对几个?
查看>>
Mysql 索引问题集锦
查看>>
Mysql 纵表转换为横表
查看>>
mysql 编译安装 window篇
查看>>
mysql 网络目录_联机目录数据库
查看>>
MySQL 聚簇索引&&二级索引&&辅助索引
查看>>
Mysql 脏页 脏读 脏数据
查看>>
mysql 自增id和UUID做主键性能分析,及最优方案
查看>>
Mysql 自定义函数
查看>>