13.POSIX信号量

一.System V信号量

可以看往期博客(了解即可,怎么使用的根据兴趣)

https://blog.csdn.net/weixin_60668256/article/details/154446673?fromshare=blogdetail&sharetype=blogdetail&sharerId=154446673&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

太麻烦了,我们直接使用POSIX信号量

二.POSIX信号量

1.信号量的讲解

我们在进行写抢票代码的时候,将我们对应的票,当作我们对应的整体使用

所以,互斥锁本身就是信号量的一种

2.POSIX信号量的接口

三.环形队列的生产消费模型

1.讲解环形队列

如果我们要设计一个环形队列的话,我们应该有两个信号量(一个data,一个space)

2.环形队列整体调用代码

cpp 复制代码
#include "RingBuffer.hpp"
#include <pthread.h>
#include <unistd.h>
#include <ctime>

using namespace RingBufferModule;

void* Consumer(void* args)
{
    RingBUffer<int>* ring_buffer = static_cast<RingBUffer<int>*>(args);
    while(true)
    {
        int data;
        ring_buffer->Pop(&data);
    }
}

void* Productor(void* args)
{
    RingBUffer<int>* ring_buffer = static_cast<RingBUffer<int>*>(args);
    int data = 10;
    while(true)
    {
       ring_buffer->Equeue(10);
    }
}

int main()
{
    RingBUffer<int>* ring_buffer = new RingBUffer<int>();//共享资源 -> 临界资源
    //单生产,单消费
    pthread_t c1,p1;
    pthread_create(&c1,nullptr,Consumer,ring_buffer);
    pthread_create(&p1,nullptr,Productor,ring_buffer);

    pthread_join(c1,nullptr);
    pthread_join(p1,nullptr);

    delete ring_buffer;
    return 0;
}

3.环形队列框架设计

cpp 复制代码
#pragma once

#include <iostream>
#include <vector>
#include <semaphore.h>

namespace RingBufferModule
{
    template<class T>
    class RingBUffer
    {
    public:
        RingBuffer()
        {
            
        }
        void Equeue(const T& in)
        {
            //生产者
        }
        void Pop(T* out)
        {
            //消费者
        }
        ~RingBuffer()
        {

        }
    private:
        std::vector<T> _ring;    //环,临界资源
        int _cap;                //容量
        int _p_step;             //生产者位置
        int _c_step;             //消费者位置
        
        sem_t _datasem;          //数据信号量
        sem_t _spacesem;         //空间信号量
    };
}

4.Sem封装

cpp 复制代码
#pragma once
#include <semaphore.h>

namespace SemModule
{
    int defalutsemval = 1;
    class Sem
    {
    public:
        Sem(int value = defalutsemval) : _init_value(value)
        {
            int n = ::sem_init(&_sem, 0, _init_value);
            (void)n;
        }
        void P()
        {
            int n = ::sem_wait(&_sem);//就是--
            (void)n;
        }
        void V()
        {
            int n = ::sem_post(&_sem);//就是++
            (void)n;
        }
        ~Sem()
        {
            int n = ::sem_destroy(&_sem);
            (void)n;
        }
    private:
        sem_t _sem;
        int _init_value;
    };
} // namespace SemModule

5.RingBuffer构造函数

cpp 复制代码
RingBuffer(int cap):
        _ring(cap),
        _cap(cap),
        _p_step(0),
        _c_step(0),
        _datasem(0),
        _spacesem(cap)
        {
            
        }

6.Equeue函数

cpp 复制代码
void Equeue(const T& in)
        {
            //生产者
            _spacesem.P();

            _ring[_p_step] = in;
            _P_step++;
            _p_step %= _cap;

            _datasem.V();
        }

7.Pop函数

cpp 复制代码
void Pop(T* out)
        {
            //消费者
            _datasem.P();

            *out = _ring[_c_step];
            _c_step++;
            _c_step %= _cap;

            _spacesem.V();
        }

8.总代码

cpp 复制代码
#pragma once

#include <iostream>
#include <vector>
#include "Sem.hpp"

namespace RingBufferModule
{
    using namespace SemModule;
    template<class T>
    class RingBuffer
    {
    public:
        RingBuffer(int cap):
        _ring(cap),
        _cap(cap),
        _p_step(0),
        _c_step(0),
        _datasem(0),
        _spacesem(cap)
        {
            
        }
        void Equeue(const T& in)
        {
            //生产者
            _spacesem.P();

            _ring[_p_step] = in;
            _P_step++;
            _p_step %= _cap;

            _datasem.V();
        }
        void Pop(T* out)
        {
            //消费者
            _datasem.P();

            *out = _ring[_c_step];
            _c_step++;
            _c_step %= _cap;

            _spacesem.V();
        }
        ~RingBuffer()
        {

        }
    private:
        std::vector<T> _ring;    //环,临界资源
        int _cap;                //容量
        int _p_step;             //生产者位置
        int _c_step;             //消费者位置
        
        Sem _datasem;          //数据信号量
        Sem _spacesem;         //空间信号量
    };
}

就相当于是,将我们对应得mutex和cond进行了封装

四.测试

1.单生产,单消费

cpp 复制代码
#include "RingBuffer.hpp"
#include <pthread.h>
#include <unistd.h>
#include <ctime>

using namespace RingBufferModule;

void* Consumer(void* args)
{
    RingBuffer<int>* ring_buffer = static_cast<RingBuffer<int>*>(args);
    while(true)
    {
        sleep(1);
        int data;
        ring_buffer->Pop(&data);
        std::cout << "消费了一个数据: " << data << std::endl;
    }
}

void* Productor(void* args)
{
    RingBuffer<int>* ring_buffer = static_cast<RingBuffer<int>*>(args);
    int data = 0;
    while(true)
    {
       ring_buffer->Equeue(data);
       std::cout << "生产了一个数据: " << data << std::endl;
       data++;
    }
}

int main()
{
    RingBuffer<int>* ring_buffer = new RingBuffer<int>(5);//共享资源 -> 临界资源
    //单生产,单消费
    pthread_t c1,p1;
    pthread_create(&c1,nullptr,Consumer,ring_buffer);
    pthread_create(&p1,nullptr,Productor,ring_buffer);

    pthread_join(c1,nullptr);
    pthread_join(p1,nullptr);

    delete ring_buffer;
    return 0;
}

为啥我们在这个代码里面,没有进行判断呢?

他将资源是否可使用,转化成是否申请成功信号量(只要申请成功就一定有资源),要等待也是在信号量上面进行等待

生产者和消费者之间的关系,由我们对应的信号量来进行维护了

2.多生产,多消费

生产者之间要加锁,由一个生产者为代表,来进入我们对应的环形队列里面(消费者也是同理)

修改代码如下:

cpp 复制代码
#pragma once

#include <iostream>
#include <vector>
#include "Sem.hpp"

namespace RingBufferModule
{
    using namespace SemModule;
    template<class T>
    class RingBuffer
    {
    public:
        RingBuffer(int cap):
        _ring(cap),
        _cap(cap),
        _p_step(0),
        _c_step(0),
        _datasem(0),
        _spacesem(cap)
        {
            pthread_mutex_init(&_p_lock);
            pthread_mutex_init(&_c_lock);
        }
        void Equeue(const T& in)
        {
            //生产者
            pthread_mutex_lock(&_p_lock);
            _spacesem.P();

            _ring[_p_step] = in;
            _P_step++;
            _p_step %= _cap;

            _datasem.V();
            pthread_mutex_unlock(&_p_lock);
        }
        void Pop(T* out)
        {
            pthread_mutex_lock(&_c_lock);
            //消费者
            _datasem.P();

            *out = _ring[_c_step];
            _c_step++;
            _c_step %= _cap;

            _spacesem.V();
            pthread_mutex_unlock(&_c_lock);
        }
        ~RingBuffer()
        {
            pthread_mutex_destroy(&_p_lock);
            pthread_mutex_destroy(&_c_lock);
        }
    private:
        std::vector<T> _ring;    //环,临界资源
        int _cap;                //容量
        int _p_step;             //生产者位置
        int _c_step;             //消费者位置
        
        Sem _datasem;            //数据信号量
        Sem _spacesem;           //空间信号量

        pthread_mutex_t _p_lock; //生产者之间的锁
        pthread_mutex_t _c_lock; //消费者之间的锁
    };
}

先申请信号量,再申请锁,还是先申请锁还是先信号量呢?

答案是先进行申请信号量,拿这信号量,再竞争锁资源(这样较快)

3.封装代码

cpp 复制代码
#pragma once

#include <iostream>
#include <vector>
#include "Sem.hpp"
#include "Mutex.hpp"

namespace RingBufferModule
{
    using namespace SemModule;
    using namespace LockModule;

    template<class T>
    class RingBuffer
    {
    public:
        RingBuffer(int cap):
        _ring(cap),
        _cap(cap),
        _p_step(0),
        _c_step(0),
        _datasem(0),
        _spacesem(cap)
        {
        }
        void Equeue(const T& in)
        {
            //生产者
            _spacesem.P();

            {
                LockGuard lockguard(_p_lock);
                _ring[_p_step] = in;
                _P_step++;
                _p_step %= _cap;
            }
            
            _datasem.V();
            
        }
        void Pop(T* out)
        {
            //消费者
            _datasem.P();

            {
                LockGuard lockguard(_c_lock);
                *out = _ring[_c_step];
                _c_step++;
                _c_step %= _cap;
            }
            
            _spacesem.V();
            
        }
        ~RingBuffer()
        {
        }
    private:
        std::vector<T> _ring;    //环,临界资源
        int _cap;                //容量
        int _p_step;             //生产者位置
        int _c_step;             //消费者位置
        
        Sem _datasem;            //数据信号量
        Sem _spacesem;           //空间信号量

        Mutex _p_lock; //生产者之间的锁
        Mutex _c_lock; //消费者之间的锁
    };
}
相关推荐
月光技术杂谈3 分钟前
Linux发展到6.0了,其在嵌入式应用中,实时性方面有没有一些改进?
linux·嵌入式·实时性
周小码8 分钟前
Spacedrive:用Rust构建的虚拟分布式文件系统
开发语言·后端·rust
小张成长计划..11 分钟前
【C++】20:set和map的理解和使用
c++
聪明努力的积极向上13 分钟前
【设计】分批查询数据通用方法(基于接口 + 泛型 + 定点复制)
开发语言·设计模式·c#
灰色人生qwer17 分钟前
VS Code 配置Java环境
java·开发语言
yyy(十一月限定版)17 分钟前
C语言——排序算法
c语言·开发语言·排序算法
tgethe18 分钟前
Java 链表(LinkedList)
java·开发语言·链表
小鸡吃米…27 分钟前
Python - 多重继承
开发语言·python
曼巴UE536 分钟前
UE C++ 字符串编码转码
c++·ue5
智驱力人工智能39 分钟前
仓库园区无人机烟雾识别:构建立体化、智能化的早期火灾预警体系 无人机烟雾检测 无人机动态烟雾分析AI系统 无人机辅助火灾救援系统
人工智能·opencv·算法·目标检测·架构·无人机·边缘计算