Linux信号量

信号量是一个计数器本质是对特定资源的预定机制。

多线程使用资源:

1.将目标资源整体使用 锁+二元信号量

2.将目标资源按照块分批使用 需要阻止过多资源同时进入,不要访问同一个位置

生产消费模型:

1.生产者和消费者是解耦的。2.支持忙闲不均。3.提高效率,不是体现在入交易场所和出交易场所上,而在于未来获取任务和处理具体任务是并发的。

现在有个环形队列用于描述生产者消费者模型,将环形队列看成长度为N的数组,指针++完之后模运算就回到原点模拟出环形队列。

生产者和消费者对应两个指针,在环形队列里,一个负责放苹果一个负责拿苹果,生产者从头开始放苹果一直到生产满了回到原点。就轮到消费者去消费拿苹果。

约定:

1.空生产者先运行

2.满消费者先运行

3.生产者不能多消费者一轮以上

4.消费者不能超过生产者

环形队列,不为空或不为满,生产和消费可以同时进行

环形队列,为空为满,生产和消费需要同步互斥

用信号量保证这四个约定。

生产者关注空位置,消费者关注有效数据

刚开始 sem_blank=N sem_data=0

模拟追逐过程:

生产者:P操作申请资源,对空格子--,空格子被占用,多了数据就要V操作去对生产者的指针++

消费者:P操作申请资源对指针++,数据--,V操作空各子++

信号量原子申请成功继续运行,申请失败申请的线程就被阻塞,数据信号量为0消费者P操作阻塞,生产者空格子多可以随时运行,所以可以继续P操作后接着运行直到N--到0信号量为0,再去P操作无法进行被阻塞,同时消费者那边的sem_data数据的信号量一直++P操作就会运行,然后就数据就去--直到为0,V操作空格子++生产者可以继续运行,这样就可以为空时生产者先运行,为满时消费者先运行且生产者不会多出消费者一轮。

cpp 复制代码
#include <iostream>
#include <semaphore.h>
#include <pthread.h>

namespace SemModule
{
    const int defaultvalue = 1;
    class Sem
    {
    public:
        Sem(unsigned int sem_value = defaultvalue)
        {
            sem_init(&_sem, 0, sem_value); // 创建信号量
        }
        void P()
        {
            int n = sem_wait(&_sem);
            (void)n;
        }
        void V()
        {
            int n = sem_post(&_sem);
        }
        ~Sem()
        {
            sem_destroy(&_sem); // 销毁信号量
        }

    private:
        // int vaule;//信号量起始地址
        sem_t _sem;
    };
};
cpp 复制代码
#pragma once

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

static const int gcap = 5; // 容量 for debug

using namespace SemModule;

template <typename T>
class RingQueue
{
public:
    RingQueue(int cap = gcap)
        : _cap(cap),
          _rq(cap),
          _blank_sem(cap),
          _p_step(0),
          _data_sem(0),
          _c_step(0)
    {
    }
    void Equeue(const T &in)
    {
        // 生产者
        // 1.申请信号量,空位置信号量
        _blank_sem.P();
        // 2.生产
        _rq[_p_step] = in;
        // 3.更新下标
        ++_p_step;
        // 4.维持环形特性
        _p_step %= _cap;
        _data_sem.V();
    }
    void Pop(T *out)
    {
        // 消费者
        // 1.申请信号量,空位置信号量
        _data_sem.P();
        // 2.消费
        *out = _rq[_c_step];
        // 3.更新下标
        ++_c_step;
        // 4.维持环形特性
        _c_step %= _cap;
        _blank_sem.V();
    }
    ~RingQueue()
    {
    }

private:
    std ::vector<T> _rq;
    int _cap; // 容量

    // 生产者
    Sem _blank_sem; // 空位置
    int _p_step;    // 生产下标位置

    // 消费者
    Sem _data_sem; // 数据
    int _c_step;   // 生产下标位置
};
cpp 复制代码
#include "RingQueue.hpp"
#include <unistd.h>
#include <pthread.h>

void *consumer(void *args)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);

    while (true)
    {
        sleep(2);
        // 1.消费任务
        int t = 0;
        rq->Pop(&t);
        // 2.处理任务
        std::cout << "消费者拿到了一个数据: " << t << std::endl;
    }
}

void *productor(void *args)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
    int data = 1;
    while (true)
    {
        // sleep(2);
        //  1.获得任务
        std::cout << "生产了一个任务: " << data << std::endl;
        // 2.生产任务
        rq->Equeue(data);

        data++;
    }
}

int main()
{
    RingQueue<int> *rq = new RingQueue<int>();

    pthread_t c[1], p[1];

    pthread_create(c, nullptr, consumer, rq);
    pthread_create(p, nullptr, productor, rq);

    pthread_join(c[0], nullptr);
    pthread_join(p[0], nullptr);

    return 0;
}

以上是单生产单消费,多生产多消费需考虑互斥关系,就要加锁。

cpp 复制代码
#pragma once

#include <iostream>
#include <pthread.h>

namespace MutexModule
{
    class Mutex
    {
    public:
        Mutex()
        {
            pthread_mutex_init(&_mutex, nullptr);
        }
        void Lock()
        {
            int n = pthread_mutex_lock(&_mutex);
            (void)n;
        }
        void Unlock()
        {
            int n = pthread_mutex_unlock(&_mutex);
            (void)n;
        }
        ~Mutex()
        {
            pthread_mutex_destroy(&_mutex);
        }
        pthread_mutex_t *Get()
        {
            return &_mutex;
        }

    private:
        pthread_mutex_t _mutex;
    };

    class LockGuard
    {
    public:
        LockGuard(Mutex &mutex):_mutex(mutex)
        {
            _mutex.Lock();
        }
        ~LockGuard()
        {
            _mutex.Unlock();
        }

    private:
        Mutex &_mutex;
    };
};
cpp 复制代码
#include "RingQueue.hpp"
#include <unistd.h>
#include <pthread.h>

void *consumer(void *args)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);

    while (true)
    {
        sleep(2);
        // 1.消费任务
        int t = 0;
        rq->Pop(&t);
        // 2.处理任务
        std::cout << "消费者拿到了一个数据: " << t << std::endl;
    }
}

void *productor(void *args)
{
    RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
    int data = 1;
    while (true)
    {
        // sleep(2);
        //  1.获得任务
        std::cout << "生产了一个任务: " << data << std::endl;
        // 2.生产任务
        rq->Equeue(data);

        data++;
    }
}

int main()
{
    RingQueue<int> *rq = new RingQueue<int>();

    pthread_t c[1], p[1];

    pthread_create(c, nullptr, consumer, rq);
    pthread_create(p, nullptr, productor, rq);

    pthread_join(c[0], nullptr);
    pthread_join(p[0], nullptr);

    return 0;
}

先申请信号量再加锁。

相关推荐
♡喜欢做梦2 小时前
MyBatis XML 配置文件:从配置规范到 CRUD 开发实践
xml·java·java-ee·mybatis
爱吃烤鸡翅的酸菜鱼2 小时前
Spring Boot 实现 WebSocket 实时通信:从原理到生产级实战
java·开发语言·spring boot·后端·websocket·spring
J不A秃V头A2 小时前
Maven的分发管理与依赖拉取
java·maven
雪域迷影2 小时前
C++中编写UT单元测试用例时如何mock非虚函数?
开发语言·c++·测试用例·gmock·cpp-stub开源项目
AI街潜水的八角3 小时前
Python电脑屏幕&摄像头录制软件(提供源代码)
开发语言·python
hadage2333 小时前
--- git 的一些使用 ---
开发语言·git·python
lly2024065 小时前
HTML与CSS:构建网页的基石
开发语言
一只会写代码的猫5 小时前
面向高性能计算与网络服务的C++微内核架构设计与多线程优化实践探索与经验分享
java·开发语言·jvm