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);
相关推荐
神梦流19 分钟前
GE 引擎的内存优化终局:静态生命周期分析指导下的内存分配与复用策略
linux·运维·服务器
凡人叶枫21 分钟前
C++中输入、输出和文件操作详解(Linux实战版)| 从基础到项目落地,避坑指南
linux·服务器·c语言·开发语言·c++
wdfk_prog26 分钟前
[Linux]学习笔记系列 -- [drivers][input]serio
linux·笔记·学习
xuhe243 分钟前
[全流程详细教程]Docker部署ClawBot, 使用GLM4.7, 接入TG Bot实现私人助理. 解决Docker Openclaw Permission Denied问题
linux·docker·ai·github·tldr
Lsir10110_1 小时前
【Linux】进程信号(下半)
linux·运维·服务器
酉鬼女又兒1 小时前
零基础入门Linux指南:每天一个Linux命令_pwd
linux·运维·服务器
云飞云共享云桌面1 小时前
高性能图形工作站的资源如何共享给10个SolidWorks研发设计用
linux·运维·服务器·前端·网络·数据库·人工智能
zl_dfq1 小时前
Linux 之 【多线程】(pthread_xxx、轻量级进程、原生线程库、线程ID、__thread、线程栈、线程与信号、线程与程序替换)
linux
choke2331 小时前
Python 基础语法精讲:数据类型、运算符与输入输出
java·linux·服务器
AZ996ZA2 小时前
自学linux的第二十一天【DHCP 服务从入门到实战】
linux·运维·服务器·php