Linux多线程知识要点

Linux多线程知识要点

线程等待

线程默认创建之后,线程有个分离属性默认为joinable状态,这个状态下的线程退出后不会自动释放资源,因此需要被其他线程等待,从而获取这个线程的退出返回值,释放资源
默认情况下

默认情况下线程退出,为了保存自己的退出返回值,因此线程占用的资源在退出之后也不会被完全释放,需要被其他线程所等待
 

线程分离

线程分离:将线程的分离属性设置为detach状态
在设计线程的时候,线程有很多属性,其中有一个叫做分离属性,分离属性默认值为-JOINABLE,表示线程退出之后不会自动释放资源,需要被等待,如果将线程的分离属性设置为其他值-DETACH,这时候线程退出之后将不需要被等待,而是直接释放资源
所以线程一旦设置了分离属性,则退出后自动释放资源,等待变得毫无意义,所以设置了分离属性的线程是不能被等待的
 
一个线程既不关心返回值,也不想等待,这种线程就适合被分离
 

线程安全

概念:多线程之间对同一个临界资源的访问操作是安全的(多线程同时修改同一个临界资源可能会造成数据二义,不安全)
实现:如何实现线程操作是安全的
互斥:保证执行流在同一时间对临界资源的唯一访问
同步:通过一些条件判断实现对资源获取的合理操作
互斥的实现:互斥锁
同步的实现:条件变量,信号量

互斥锁:

  本质:就是一个0/1的状态器,主要用于标记资源的访问状态0-不可访问,1-可访问
  操作:加锁,解锁
  流程:一个执行流在访问资源之前进行加锁操作,如果不能加锁则阻塞,在访问资源完毕之后解锁(比如上厕所之前要锁门)
互斥锁实现互斥,本质上自己也是一个临界资源(同一个资源的所有线程在访问的时候必须加同一把锁)
因此互斥锁必须先保证自己是安全,互斥锁本身的操作是原子操作
接口:
  1.定义互斥锁变量:pthread_mutex_t mutex;
  2.初始化互斥锁:pthread_mutex_init
  3.加锁:pthread_mutex_lock/pthread_mutex_trylock(非阻塞加锁)
  4.解锁:pthread_mutex_unlock();
  5.释放销毁:pthread_mutex_destory()
注意点:
  1.使用锁过程中,加锁后,在任意有可能退出函数或线程的位置都要解锁
  2.锁只能保证安全操作,不能保证操作合理

读写锁等多种锁

实现读共享,写互斥,应用于读者写者模型
可以同时加读锁访问数据,但是写锁互斥,当写的时候别人既不能读也不能写
实现原理:通过两个计数器来实现(读者计数,写者计数)
加锁依靠自旋锁来实现
自旋锁:不断循环判断进行条件判断的锁(不释放cpu),即时性强
适用场景:比较明确等待时间比较短的场景
悲观锁:总是认为访问期间会有冲突,因此总是加锁保护
乐观锁:总是认为访问期间大概率没有冲突
可重入锁:同一个线程可以重复加锁
不可重入锁:同一个线程也不能重复加锁,一个概念:程序流程无法继续推进,卡死的情况叫做死锁
死锁的产生:由于对于锁资源的争抢不当所导致
四个必要条件:
  1.互斥条件:我加的锁,别人不能继续加
  2.不可剥夺条件:我加的锁别人不能解,只有自己能解
  3.请求与保持条件:加A锁后,请求B锁,B锁请求不到不释放A
  4.环路等待条件:加A请求B,对方加B请求A
死锁的可能:
  1.加解锁顺序不一致
  2.阻塞加锁
预防:编写代码过程中破坏死锁产生的必要条件(破坏条件3与条件4)
  1.加解锁顺序保持一致
  2.使用非阻塞加锁,加不了锁释放已有
避免:避免产生死锁的一些具体解决方案
  银行家算法
  1.定义系统运行状态:安全or非安全
  2.定义表:所有资源表,已分配资源表,资源请求表
  3.思想:查看资源请求表,哪个线程要请求哪个锁,根据前两张表判断,这个锁分配给线程是否有可能造成环路等待,有可能则不予分配
 

同步的实现

概念:通过一些条件判断,保证执行流对资源获取的合理
实现:条件变量,信号量
条件变量本质:提供了一个pcb等待队列以及使线程阻塞和唤醒线程的两个接口
(条件变量是搭配互斥锁一起使用的)
 
接口认识:
1.定义条件变量:pthread_cond_t cond
2.初始化:pthread_cond_init
3.阻塞:pthread_cond_wait/pthread_cond_timedwait
4.唤醒:pthread_cond_signal(唤醒至少一个)/pthread_cond_broadcast(唤醒所有)
5.销毁:pthread_cond_destory
注意事项:
1.条件的合理判断需要循环进行
2.多种角色需要使用多个条件变量分开等待分开唤醒
如何阻塞一个线程?
将pcb的状态置位可中断休眠,置一个唤醒条件
 

生产者与消费者模型(容易出现手撕代码题)

一种特殊的设计模式
设计模式:大佬们针对典型应用场景设计的解决方案
针对场景:有大量数据的产生以及处理的场景
思想:将产生与处理进行模块分离,中间使用线程安全的缓冲区进行交互
1.解耦合
2.支持忙闲不均(数据缓冲队列)
3.支持并发(缓冲队列必须线程安全)
实现:
  生产者与消费者无非就是两类执行流+线程安全的任务队列

信号量(POSIX)

本质:就是一个计数器
操作:
p操作:计数-1,判断计数是否大于等于0,成立则返回否则阻塞
v操作:计数+1,唤醒一个阻塞的进程或线程
同步的实现:
通过计数器对资源数量进行计数,获取资源前进行p操作,产生资源后进行v操作,通过这种方式实现对资源的合理获取
互斥的实现:
计数器初始值为1(资源只有一个),访问资源前进行p操作,访问完毕后进行v操作,实现类似加锁和解锁的操作
接口流程认识:
1.定义:sem_t sem;
2.初始化:int sem_init(sem_t *sem,int pshared,int value)
sem:定义的信号量
pshared:0-进程间;1-进程间
value:信号量初值–有多少资源初值就设置为多少
3.p操作
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_(sem_t *sem);
4.v操作
int sem_post(sem_t *sem);
5.销毁
int sem_destory(sem_t *sem);

信号量与条件变量实现同步上的区别

  1. 本质上的不同,信号量是个计数器,条件变量没有计数器,因此条件变量的资源访问合理性需要用户自己进行。但是信号量则可以通过自身计数完成
  2. 条件变量需要搭配互斥锁一起使用,信号量则不需要

线程池

思想:线程池其实是一堆创建好的线程+任务队列
有任务来了则抛入线程池中,分配一个线程进行处理
1.节省了任务处理过程之中线程的创建于销毁成本
2.线程池中的线程与任务节点数量都有最大限制,避免资源耗尽的风险
适用场景:有大量任务需要处理的场景

  1. 成本:一个任务处理的成本
    线程创建时间+任务处理时间+线程销毁时间=总耗时
    如果任务处理时间较短,则大量时间被现成的创建与销毁所消耗
  2. 风险:若线程无限制,则在峰值压力下会有资源耗尽系统崩溃风险
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值