前言:
这篇文章我们来讲讲Linux——POSIX信号量:
个人简介:努力学习ing
个人专栏:Linux
CSDN主页 愚润求学
其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏
之前,我们讲述过System V版本的信号量(用于进程)
POSIX版本的信号量(可用于线程),如果要用于进程可以强转,比如把共享资源的前面一部分内容直接强转成POSIX版本的信号量(相当于顶替定义操作了)
--
,申请信号量)++
,释放信号量)0
),则该线程会被加入到信号量的等待队列中。(直到有线程 V 操作把信号量给放出来了)初始化
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem_t
信号量的类型sem
:要初始化的信号量pshared
:零表示线程间共享,非零表示进程间共享(传0
就行)value
:信号量初始值销毁
int sem_destroy(sem_t *sem);
等待
int sem_wait(sem_t *sem); // P 操作
0
,线程就会被放到信号量等待队列里面等while
)发布
int sem_post(sem_t *sem); // V 操作
问题描述
问题抽象
模型特点
实现
vector
来模拟环形队列,生产和消费往vector
对应下标里进行代码实现
RingQueue.hpp文件:
#include
#include
#include
#include
using namespace std;
template <typename T>
class RingQueue
{
public:
RingQueue(int size)
:_cap(size),
_p_step(0),
_c_step(0)
{
_ringqueue.resize(size);
sem_init(&_empty_sem, 0, size);
sem_init(&_noempty_sem, 0, 0); // 初始时,没有非空位置
pthread_mutex_init(&_mutex_p, nullptr);
pthread_mutex_init(&_mutex_c, nullptr);
}
~RingQueue()
{
sem_destroy(&_empty_sem);
sem_destroy(&_noempty_sem);
pthread_mutex_destroy(&_mutex_p);
pthread_mutex_destroy(&_mutex_c);
}
void Push(const T &data)
{
// 1. 申请信号量
sem_wait(&_empty_sem); // 如果失败就阻塞
// 多生产之间 要加锁
pthread_mutex_lock(&_mutex_p);
// 2. 生产
_ringqueue[_p_step] = data;
// 3. 生产者位置改变(并维护环形队列)
_p_step = (_p_step + 1) % _cap;
// 4. 改变 _noempty_sem,通知消费者
sem_post(&_noempty_sem);
// 解锁
pthread_mutex_unlock(&_mutex_p);
}
void Pop(T* data)
{
// 1. 申请信号量
sem_wait(&_noempty_sem); // 如果失败就阻塞
// 多消费者之间 要加锁
pthread_mutex_lock(&_mutex_c);
// 2. 消费
*data = _ringqueue[_c_step];
// 3. 消费者位置改变(并维护环形队列)
_c_step = (_c_step + 1) % _cap;
// 4. 改变 _noempty_sem,通知生产者
sem_post(&_empty_sem);
// 解锁
pthread_mutex_unlock(&_mutex_c);
}
private:
vector<T> _ringqueue;
int _cap;
sem_t _empty_sem; // 描述空位置
sem_t _noempty_sem;
int _p_step; // 生产者所在位置
int _c_step;
// 多生产,多消费需要加锁,维护生产者和生产者,消费者和消费者之间的互斥关系
pthread_mutex_t _mutex_p;
pthread_mutex_t _mutex_c;
};
Main.cpp文件
#include "RingQueue.hpp"
#include
#include
int num = 1;
void* Pro(void* args)
{
RingQueue<int>* p = static_cast<RingQueue<int>*>(args);
while(true)
{
// sleep(2);
p->Push(num);
cout << "生产了一个任务: " << num <<endl;
num++;
}
}
void* Com(void* args)
{
RingQueue<int>* p = static_cast<RingQueue<int>*>(args);
while(true)
{
sleep(2);
int ret;
p->Pop(&ret);
cout << "消费了一个任务: " << ret << endl;
}
}
int main()
{
int num = 0;
RingQueue<int> super(5);
// 单生产,单消费
// pthread_t p[1], c[1];
// pthread_create(&p[0], nullptr, Pro, &super);
// pthread_create(&c[0], nullptr, Com, &super);
// pthread_join(p[0], nullptr);
// pthread_join(c[0], nullptr);
// 多多
pthread_t p[3], c[2];
pthread_create(&p[0], nullptr, Pro, &super);
pthread_create(&p[1], nullptr, Pro, &super);
pthread_create(&p[2], nullptr, Pro, &super);
pthread_create(&c[0], nullptr, Com, &super);
pthread_create(&c[1], nullptr, Com, &super);
pthread_join(p[0], nullptr);
pthread_join(p[1], nullptr);
pthread_join(p[2], nullptr);
pthread_join(c[0], nullptr);
pthread_join(c[1], nullptr);
}
运行结果:
单生产单消费,且消费更快,则:生产一个消费一个
cout
输出到屏幕上也有并发问题。
多生产,多消费(生产慢,消费快):
对全局变量num
的++
操作不是原子的,也会有并发问题。
但是我们基本可以看出来消费的都是旧任务,生产的是新任务。
多多模型相比单单模型,缺少的就是3个关系中的前两个
多多模型,加锁的位置
互斥锁 + 条件变量
queue
作为交易场所,但是queue
不能同时往多个地方入队,所以只能整体使用信号量
size
为 1
,则这时候也就退化成了互斥锁 + 条件变量模型进一步封装
{}
扩起来,在{}
内定义的变量声明周期随{}
我的分享也就到此结束啦
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
公主,王子:点赞→收藏⭐→关注
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!