信号量

POSIX信号量和SystemV信号量作⽤相同,都是⽤于同步操作,达到⽆冲突的访问共享资源⽬的。但POSIX可以⽤于线程间同步。

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

多线程的使用场景:

1.将目标资源整体使用【mutex+2元信号量】1.将目标资源整体使用【mutex+2元信号量】

2.将目标资源按照不同的块分批使用[信号量]

#include
#include
#include
namespace SemModule{
    class Sem{
        public:
            Sem(unsigned int value=1){
                sem_init(&sem_,0,value);
            }
            ~Sem(){
                sem_destroy(&sem_);
            }
            void P(){
                sem_wait(&sem_);//原子操作
            }
            void V(){
                sem_post(&sem_);
            }
        private:
            sem_t sem_;
    };
}

接口介绍:

初始化信号量

#include

int sem_init(sem_t *sem, int pshared, unsigned int value);

参数:pshared:0表⽰线程间共享,⾮零表⽰进程间共享

value:信号量初始值

销毁信号量 :
int sem_destroy(sem_t *sem);
等待信号量 :

功能:等待信号量,会将信号量的值减1

int sem_wait(sem_t *sem); //P()
发布信号量
功能:发布信号量,表⽰资源使⽤完毕,可以归还资源了。将信号量值加1。

int sem_post(sem_t *sem);//V()
下面我们先基于几个函数去封装一个信号量

下面我以一个例子去带领大家了解信号量

基于环形队列的生产者消费者模型:

约定1:空,生产者先运行

约定2:满,消费者先运行

约定3:生产者不能把消费者套一个圈以上

约定4:消费者,不能超过生产者

#pragma once
#include
#include"Sem.hpp"
static const int gcap=5;
using namespace SemModule;
template 
class RingQueue {
    private:
       std::vector_rq;
        int _cap;
        Sem _blank_sem;//表示空的资源
        Sem _data_sem;//表示数据的资源
        int _p_step;//消费者位置
        int _c_step;//生产者位置
    public:
       RingQueue(int size = gcap)
    : _rq(size),
      _cap(size),
      _blank_sem(size),   // 空位一开始是满的
      _data_sem(0),       // 数据一开始是空的
      _p_step(0),
      _c_step(0) {}

void Enqueue(const T& in) {
    _blank_sem.P();          // 申请空位
    _rq[_p_step] = in;
    _p_step = (_p_step + 1) % _cap;
    _data_sem.V();           // 释放数据
}

void Pop(T& out) {
    _data_sem.P();           // 申请数据
    out = _rq[_c_step];
    _c_step = (_c_step + 1) % _cap;
    _blank_sem.V();          // 释放空位
}

};

总结:如果资源可以拆分,你可以考虑sem

如果资源是整体使用的,你就使用mutex

用通俗易懂的话来说:

信号量(semaphore)就是内核(或库)帮你保管的一个“非负整数计数器”+“睡眠/唤醒”机制;P 操作把计数器减 1,如果结果 <0 就让调用者睡进去;V 操作把计数器加 1,如果结果 ≤0 就拉一个睡着的人起来。