1 认识信号量
前面的初识信号量
- 信号量(信号灯):本质就是一个计算器
- 信号量需要进行PV操作,P == -- ;V == ++ ,原子的!
- 信号量是用来描述临界资源中资源数目的
- 根据资源数目分成两类
- 一个资源(整体资源):二元信号 == 互斥锁
- 剩下就是多元信号
- 每一个线程,在访问对应的资源的时候,先申请信号量,申请成功,表示允许该线程使用该资源,申请不成功,目前无法使用该资源!
- 信号量的工作机制:信号量机制类似电影票的购买,是一种资源预定机制。
- 信号量已经是资源的计数器了,申请信号量成功,本身就表明资源可用!申请信号量失败,本身就表明资源不可用 -- 资源是否存在的判断 就转化成为信号量的申请行为!
2 认识POSIX信号量的接口
初始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
pshared:0表示线程间共享,非零表示进程间共享
value:信号量初始值
销毁信号量
int sem_destroy(sem_t *sem);
等待信号量
功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); //P()
发布信号量
功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);//V()
上一节生产者-消费者的例子是基于queue的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序(POSIX信号量):
3 基于环形队列的生产消费模型
3.1 结构模型分析
数据结构:循环队列的分析
队列是一个固定大小的队列,通过vector容器加循环下标来实现
构建cp问题
- 消费者和生产者关心的问题是一样的吗?答:不一样,生产者关心空间,消费者关心数据
- 只要信号量不为0,表示资源可用,线程可以访问。
- 环形队列,只要我们访问不同的区域,生产者和消费者可以同时进行。
- 消费者和生产者什么时候在同一区域上?
- 两个人刚开始,没有数据的时候--空
- 环形队列,数据布满--满
- 指向同一个区域,两者之间存在竞争关系,应该让谁先运行?
- 空的时候,生产者
- 满的时候,消费者
消费者任务:向head中pop数据;生产者任务:向tail中push数据
游戏规则:
- 空时让生产者先走
- 满时让消费者先走
- 消费者不能超过生产者
- 生产者不能套圈消费者
3.2 伪代码
3.3 具体实现
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
const int N = 5;
template <class T>
class RingQueue
{
private:
void P(sem_t *sem)
{
sem_wait(sem);
}
void V(sem_t *sem)
{
sem_post(sem);
}
void lock(pthread_mutex_t *mutex)
{
pthread_mutex_lock(mutex);
}
void unlock(pthread_mutex_t *mutex)
{
pthread_mutex_unlock(mutex);
}
public:
RingQueue(int num = N) : _capacity(num), _v(5), _head(0), _tail(0)
{
pthread_mutex_init(&_consumer_mutex, nullptr);
pthread_mutex_init(&_producer_mutex, nullptr);
sem_init(&_sem_data, 0, 0);
sem_init(&_sem_room, 0, N);
}
~RingQueue()
{
pthread_mutex_destroy(&_consumer_mutex);
pthread_mutex_destroy(&_producer_mutex);
sem_destroy(&_sem_data);
sem_destroy(&_sem_room);
}
void push(const T &task)
{
P(&_sem_room);
lock(&_producer_mutex);
_v[_tail++] = task;
_tail %= _capacity;
unlock(&_producer_mutex);
V(&_sem_data);
}
void pop(T *out)
{
P(&_sem_data);
lock(&_consumer_mutex);
*out = _v[_head++];
_head %= _capacity;
unlock(&_consumer_mutex);
V(&_sem_room);
}
private:
std::vector<T> _v;
int _capacity;
int _head;
int _tail;
sem_t _sem_data;
sem_t _sem_room;
pthread_mutex_t _consumer_mutex;
pthread_mutex_t _producer_mutex;
};