生产消费者模型:
顾客(消费者)超市(缓冲区)厂商(生产者)
关系:
消费者和消费者 互斥 (两个顾客争抢最后一件商品 只能有一个消费者线程)
生产者和生产者 互斥 (两个生产同一件商品的厂商争抢同一个超市上货位置 只能有一个生产者线程)
消费者和生产者 互斥&同步 (刚上货就买了(同步) 没货也买(互斥))
总结:
三种关系 两个角色 一个场景
特点:
生产者和消费者相互影响(高度耦合)缓冲区可以很好的调顺两者的关系(解耦)
类别:
1.多生产多消费
2.生产者-》阻塞队列(queue)-》生产者-》阻塞队列-》消费者
3.环形队列
线程同步:
概念:多个线程排队访问资源
同一个资源多个线程访问可能会出现问题(资源更改什么的)就会设置一个等待区让线程排队 自己的条件变量条件满足后 就可以进去啦
排队是为了防止出现饥饿问题 就是只有一个线程无限访问资源 其他线程因为速度问题 每次都抢不过最快那个线程
条件变量:
创建 初始化 销毁:
#include <pthread.h>
pthread_cond_t a = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *restrict a,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *a);
等待队列必须搭配互斥锁 因为这样才能有等待的效果
等待队列:
int pthread_cond_wait(pthread_cond_t *restrict a, pthread_mutex_t *restrict mutex);
条件满足(唤醒):
int pthread_cond_signal(pthread_cond_t *a);
信号量:
概念:计数器 计算临界资源数量大小
本质:对临界资源中的小块资源预定
操作:
访问临界资源某一块资源 信号量减一
访问完毕 信号量加一 (这两个操作必须是原子性的)
每块小资源有且仅有一个线程访问
优化点:
1.线程可以不用访问就知道临界资源剩余量(之前想知道临界资源剩余资源的唯一办法就是访问临界资源)
2.临界资源不同区域可以被多线程访问
(接口不想写 我主打简洁 😜)可以看这个大佬的
锁:
自旋锁:申请到可以访问临界资源 申请不到继续申请
挂起等待锁(互斥锁):申请到访问 无则挂起等待(阻塞队列)合适的时候再申请
合适的时间:资源满足条件 例如 资源被释放(其他线程访问完毕)超时 中断
读写锁:类似与生产消费者模型 只是读者不会拿走资源
临界区状态 无锁 读锁 写锁
读者 可以访问 可以访问 阻塞
写者 可以访问 阻塞 阻塞
读写锁原理:
读者逻辑:先创建读锁 读者数量加一 读加锁 判断读者是否唯一 则写加锁 读解锁 读者读 读加锁 读者数量减一 如果读者为零 则写解锁 后读解锁(我也不认识读这个字了hh)
写者逻辑:创建写锁 写加锁 写着写 写解锁(写的这么少怎么也不认识了www)
这种是读者优先 也有写着优先的 其实是有接口的:
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t* attr, int pref);
pref:对应三种选择
PTHREAD_RWLOCK_PREFER_READER_NP:读者优先 可能会导致写者饥饿(默认设置)
PTHREAD_RWLOCK_PREFER_WRITER_NP:写者优先
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:写者优先 写者不能递归加锁
条件变量 信号量 锁 小总结:
条件变量 信号量用于进程间通知和唤醒
互斥锁 读写锁用于对共享资源的安全访问
条件变量需要搭配互斥锁使用,信号量不需要(仅仅做出判断)
信号量既可以实现同步还可以实现互斥(自带计数器可以实现条件判断进行操作)
条件变量在等待被唤醒时需要重新对条件进行判断(循环判断 因为多线程访问)
mutex(挂起等待锁),cond(条件变量),sem(信号量) 这三个是常用的
无锁化编程方法:
原子性操作 只有一个生产者和消费者 rcu(版本更新)cas(自认为不会更改数据)
线程池:
概念:和内存池类似 阻塞队列里有一堆线程等待使用
线程池作用:
降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗
提高线程的可管理性:线程池可以统一管理、分配、调优和监控
降低程序的耦合程度: 提高程序的运行效率
线程池的关键参数:
线程池中线程最大数量(防止资源耗尽 过多性能降低)
线程安全的阻塞队列(排队缓冲)
线程池中线程的存活时间(长时间空闲浪费资源)
线程池中阻塞队列的最大节点数量(排队过多 消耗资源)
效率:多线程运行效率正态分布 达到临界值崩溃
线程池作用和原理:
线程池通过一个线程安全的阻塞任务队列加上一个或一个以上的线程实现
线程池中的线程可以从阻塞队列中获取任务进行任务处理,当线程都处于繁忙状态时可以将任务加入阻塞队列中,等到其它的线程空闲后进行处理。
可以避免大量线程频繁创建或销毁所带来的时间成本,也可以避免在峰值压力下,系统资源耗尽的风险;并且可以统一对线程池中的线程进行管理,调度监控。
其实这个理论就这些(个人目前能力来看)还是希望大家能亲自实践下 大家可以参考下这个大佬
【Linux学习】多线程——线程池 | 单例模式_单例线程池-CSDN博客https://blog.csdn.net/weixin_63726869/article/details/131156217
单例模式:
概念:
1.单例类只能有一个实例
2.单例类必须自己创建自己的唯一实例
3.单例类必须给所有其他对象提供这一实例。
主要分为饿汉模式和懒汉模式
1.
在类加载时就创建实例对象 并且在整个程序运行期间都存在
在类的静态成员变量中直接创建实例对象 并且提供一个公共的静态方法来获取该实例
优点:实现简单 线程安全 不会出现多线程并发访问的问题
缺点:在程序启动时就创建实例对象 可能会造成资源浪费
2.
在第一次使用时才创建实例对象
在类的静态成员变量中不直接创建实例对象,而是在获取实例的方法中进行判断,如果实例为null,则创建实例
优点:延迟加载,节省资源
缺点:在多线程并发访问时,可能会出现创建多个实例的问题,需要考虑线程安全性
线程安全概念 实现:
多线程编辑中 防止出现二义性和混乱 通过条件变量和互斥 信号量实现