Linux多线程——线程互斥与同步和其他概念

文章目录

线程互斥

我们之前介绍过互斥的概念,但没有介绍Linux中线程互斥的操作

互斥存在的必要性是因为访问共享资源时,有可能被CPU换下,这样就会产生bug

例如说我们上一篇中说过的抢票,我们虽然设置的是当ticket小于等于0时结束线程

但是当五个线程几乎同时进入这个判断,就会导致过量运行

主要是因为这些操作并非原子的,不是原语

要解决这个问题就需要互斥锁

互斥锁的使用

互斥锁的使用一般分为四个步骤

  1. 初始化互斥锁
  2. 临界区前加锁
  3. 临界区后解锁
  4. 用完后销毁
初始化互斥锁

这里有两个方法,第一个是静态分配

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

第二个是动态分配

int pthread_mutex_init(pthread_mutex_t* restrict mutex, const pthread_mutexattr_t* restrict attr)

第一个是要初始化的互斥量,我们在调用之前声明一共传入即可,第二个我们传入NULL

加锁和解锁

int pthread_mutex_lock(pthread_mutex_t* mutex)

int pthread_mutex_unlock(pthread_mutex_t* mutex)

当原来的互斥量没有被加锁,此时加锁就会成功

当互斥量已经被上锁了,这个线程就会被阻塞,等待互斥量解锁

销毁互斥锁

静态的互斥锁不需要销毁

已经加锁的互斥量不允许被销毁

已经销毁的互斥量,要确保以后不会再被用到

int pthread_mutex_destory(pthread_mutex_t* mutex)

这里互斥锁的库要直接使用也是不方便的,我们同样可以给其封装成类会好用很多

线程死锁

死锁指的是一个线程或者几个线程互相阻塞等待,没有任何一个线程在运行

要避免陷入死锁可以尝试破坏以下几个条件

  • 互斥条件
  • 请求与保持:一个执行流被阻塞时依然占据其保持的其他资源
  • 不剥夺条件:一个执行流获得的资源在未使用完之前不能被强行剥夺
  • 循环等待条件

线程安全与可重入

线程安全指的是在多个线程并发执行同一段代码时,不会出现不同的结果

这里主要指的是对于全局变量和静态变量

重入指的是,同一个函数被不同的执行流调用时,当一个执行流还没有执行完成,另一个执行流就再次进入的情况。一个函数可重入就说明可以同时搭载多个执行流而不出现bug,否则就是不可重入函数

实际上可重入的条件比线程安全更加严格

下面的情况出现,就说这个函数不可重入,否则会出现bug

  • 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
  • 标准IO库函数,标准IO库的很多实现都已不可重入的方式使用全局数据结构
  • 可重入函数体内使用了静态的数据结构

一般来说,函数可重入,那么线程一定是安全的,如果函数不可重入,那么就不允许多线程并发使用

线程同步

一方面是因为有可能资源被一个线程霸占,另一方面我们可能希望执行流有先后的执行顺序

同步就是让线程的执行流有先后的执行顺序

竞态条件指的是,因为时序问题,导致的程序问题

线程同步的接口

与互斥类似,线程同步也分为四部

  1. 初始化条件变量
  2. 利用条件变量等待资源就绪
  3. 资源就绪后唤醒线程来访问
  4. 使用后销毁条件变量
初始化条件变量
c 复制代码
pthread_cond_t cond;//定义变量后再初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

cond是要初始化的条件变量

attr传入NULL

利用条件变量等待资源就绪
c 复制代码
int pthread_cond_wait(pthread_cond_t* restrict cond, pthread_mutex_t* restrict mutex)

要在cond这个条件变量上等待,mutex是互斥量

进程资源等待的前提是,拿到锁进入临界区,所以wait函数需要条件变量和互斥锁

唤醒等待
c 复制代码
int pthread_cond_broadcast(pthread_cond_t* cond);
int pthread_cond_signal(pthread_cond_t* cond);

第一个是唤醒所有线程,是广播

第二个是唤醒某一个线程

销毁
c 复制代码
int pthread_cond_destory(pthread_cond_t* cond);
相关推荐
滴水之功22 分钟前
VMware OpenWrt怎么桥接模式联网
linux·openwrt
ldinvicible41 分钟前
How to run Flutter on an Embedded Device
linux
YRr YRr1 小时前
解决Ubuntu 20.04上编译OpenCV 3.2时遇到的stdlib.h缺失错误
linux·opencv·ubuntu
认真学习的小雅兰.2 小时前
如何在Ubuntu上利用Docker和Cpolar实现Excalidraw公网访问高效绘图——“cpolar内网穿透”
linux·ubuntu·docker
zhou周大哥2 小时前
linux 安装 ffmpeg 视频转换
linux·运维·服务器
不想起昵称9292 小时前
Linux SHELL脚本中的变量与运算
linux
the丶only3 小时前
单点登录平台Casdoor搭建与使用,集成gitlab同步创建删除账号
linux·运维·服务器·docker·gitlab
枫叶红花4 小时前
【Linux系统编程】:信号(2)——信号的产生
linux·运维·服务器
_微风轻起4 小时前
linux下网络编程socket&select&epoll的底层实现原理
linux·网络