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; //消费者之间的锁
    };
}
相关推荐
熬夜敲代码的小N2 小时前
仓颉ArrayList动态数组源码分析:从底层实现到性能优化
数据结构·python·算法·ai·性能优化
NiKo_W2 小时前
Linux 进程间关系与守护进程
linux·运维·服务器·进程·守护进程·会话
pale_moonlight2 小时前
五、Hbase基于环境搭建
linux·数据库·hbase
唆了蜜.2 小时前
ESLint: Expected indentation of * spaces but found *. (style/indent)
开发语言·javascript·vue·webstorm
Nie_Xun2 小时前
Ubuntu 安装与 NVIDIA 显卡驱动配置 2篇
linux·运维·ubuntu
生信小窝2 小时前
基于R获取全球海岸线数据获取与导出
开发语言·r语言
HIT_Weston2 小时前
25、【Ubuntu】【远程开发】内网穿透:密钥算法介绍(一)
linux·运维·tcp/ip·ubuntu
9ilk2 小时前
【基于one-loop-per-thread的高并发服务器】--- 自主实现HttpServer
linux·运维·服务器·c++·笔记·后端
HMS Core2 小时前
【FAQ】HarmonyOS SDK 闭源开放能力 — Push Kit
linux·python·华为·harmonyos