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);
相关推荐
VNDR26 分钟前
工欲善其事,必先利其器
linux
心灵宝贝1 小时前
Fedora 38 安装 perl-JSON RPM 包步骤(含依赖问题解决及附安装包)
linux
lilili也1 小时前
Linux——0:安装与配置、命令、gcc
linux
is08152 小时前
全志 H3 armbian 备份
linux·服务器·网络
iconball3 小时前
个人用云计算学习笔记 --18(NFS 服务器、iSCSI 服务器)
linux·运维·笔记·学习·云计算
广药门徒3 小时前
Linux驱动开发与BuildRoot是什么关系与其的应用场景
linux·运维·驱动开发
czhc11400756633 小时前
Linux108 shell:.bashrc 正则表达式:. * .* ^ $ ^$ [ ] [^] ^[] ^[^ ] \< \>
linux·正则表达式
野猪疯驴3 小时前
Linux shell学习(更新中....)
linux·shell
努力学习的小廉4 小时前
深入了解linux网络—— TCP网络通信(下)
linux·网络·tcp/ip
摇滚侠4 小时前
Spring Boot中使用线程池来优化程序执行的效率!笔记01
java·spring boot·多线程