【Linux】生产消费模型实践 --- 基于信号量的环形队列
基于信号量的环形队列
- 1 信号量
- 2 框架构建
- 3 代码实现
- 4 测试运行
1 信号量
信号量本质是一个计数器,可以在初始化时对设置资源数量,进程 / 线程 可以获取信号量来对资源进行操作和结束操作可以释放信号量!
用于多进程 / 多线程 对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。 在资源只有一个时就一把互斥锁!
信号量只能进行两种操作获取等待和释放信号,即PV操作:
- P(sv):我们将申请获取信号量称为P操作,申请信号量的本质就是申请获得临界资源中某块资源的使用权限,当申请成功时临界资源中资源的数目应该减去一。所以P操作的本质就是让计数器减一,如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行。对应的接口为,使用很简单:
#include
//阻塞等待获取 int sem_wait(sem_t *sem); //只进行一次获取,非阻塞等待 int sem_trywait(sem_t *sem); //时间片内进行等待,超出就退出阻塞! int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); - V(sv):我们将释放信号量称为V操作,释放信号量的本质就是归还临界资源中某块资源的使用权限,当释放成功时临界资源中资源的数目就应该加一。所以V操作本质就是让计数器加一,如果有其他进程 / 线程因等待sv而被挂起,就发送信号让它恢复运行,如果没有进程 / 线程因等待信号量而挂起,就给他加1。对应接口为:
#include
//释放获取的信号量 int sem_post(sem_t *sem);
PV操作都是原子的,不用担心线程安全!此外信号量初始化和销毁的接口是:
- 信号量初始化:
参数分别为:#include
int sem_init(sem_t *sem, int pshared, unsigned int value); - sem_t *sem:传入信号量的地址
- pshared:传入0值表示线程间共享,传入非零值表示进程间共享。
- value:信号量的初始值(计数器的初始值)。
- 信号量销毁:
#include
int sem_destroy(sem_t *sem);
2 框架构建
-
环形队列的成员变量
- 线性容器vector模拟环形队列
- 最大容量
int _max_step
- 消费者位置
_c_step
与 生产者位置_p_step
- 两个信号量来表示生产与消费的剩余容量
sem_t _data_sem
: 当前有多少数据
sem_t _space_sem
: 当前剩余空间还有多少
-
构造函数初始化
- 最大容量需要给值初始化
- 两个初始位置都为 0
- 信号量初始化 sem_init() 数据为 0 ,空间为 最大容量
-
Push接口用来加入数据
- 首先需要申请信号量 P 来对空间信号量进行获取 sem_wait (&sem_t _space_sem)(申请信号量是原子的)
获取信号量的本质是对资源 – - 生产进行插入 , 对应下标向后移动 , 注意不能越界
- 最后进行释放信号量 V 来对资源信号量进行释放 sem_post()
释放信号量的本质是对资源 ++
- 首先需要申请信号量 P 来对空间信号量进行获取 sem_wait (&sem_t _space_sem)(申请信号量是原子的)
-
Pop接口用来获取数据
- 首先需要申请信号量 P 来对资源信号量进行获取 sem_wait (&sem_t _space_sem)(申请信号量是原子的)
获取信号量的本质是对资源 – - 获取队列资源,并进行释放, 对应下标向后移动 , 注意不能越界
- 最后进行释放信号量 V 来对空间信号量进行释放 sem_post()
释放信号量的本质是对资源 ++
- 首先需要申请信号量 P 来对资源信号量进行获取 sem_wait (&sem_t _space_sem)(申请信号量是原子的)
-
多生产多消费改造:多个生产 / 消费线程存在 消费对消费 生产对生产的问题!
本文地址:https://www.vps345.com/9091.html