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; //消费者之间的锁
    };
}
相关推荐
曾几何时`2 分钟前
基于VM虚拟机 ubuntu使用主机代理
linux·服务器·ubuntu
Fcy6482 分钟前
C++ 模版(进阶)(含array解析)
开发语言·c++·stl·array·模版
Minecraft红客4 分钟前
复原大唐3d更新版本
c++·游戏·3d·娱乐
OKkankan8 分钟前
多态概念及使用
开发语言·数据结构·c++·算法
hudawei9968 分钟前
kotlin协程编译成Android字节码后是什么样的
android·开发语言·kotlin
nn在炼金11 分钟前
FlashAttention 1 深度解读:原理、价值、应用与实战
人工智能·算法
小周码代码19 分钟前
js 数字金额转为大写 js 金额转大写
开发语言·前端·javascript·js工具
WHS-_-202221 分钟前
Channel Estimation for mmWave High-Mobility Systems With 5G New Radio OFDM (I)
算法·5g
行走在电子领域的工匠21 分钟前
台达ST:自定义串行通讯传送与接收指令COMRS程序范例五
开发语言·台达plc·st语言编程·串口自定义协议