POSIX信号量以及读写者模型/环形队列

POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步,他的本质是一个计数器,对共享资源进行等待或释放


POSIX信号量的重要概念

1.计数器:信号量维护一个计数器,它表示当前可用的资源数量

2.释放 **P操作:**尝试将信号量的值减1。如果信号量的值大于0,则减1并继续执行。如果信号量的值为0,则阻塞等待,直到信号量的值大于0。

3.等待 V操作:将信号量的值加1。如果有线程或进程在等待信号量,则唤醒其中一个。


POSIX信号量的种类

二元信号量 :当计数器只能为1或0时,就是一个互斥锁,0就是阻塞,1就是非阻塞

**计数信号量:**当计数器大于1时,表示资源的可用数量,这个计数器表示可以同时访问共享资源的线程或进程的数量


POSIX信号量API使用

头文件包括

#include <semaphore.h>

1.sem_init

sem_init用于初始化信号量计数器的多少

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

sem: 指向信号量的指针

pshared: 决定是否在进程间共享 若为0 则在线程进行共享 若不为0 则在进程之间共享

value: 信号量的多少 决定为二元信号量还是计数信号量

2.sem_wait

sem_wait(sem_t * sem)

对信号量进行P操作,等待

sem:指向信号量的指针

3.sem_post

sem_post(sem_t * sem)

对信号量进行V操作,释放

sem:指向信号量的指针

4.sem_destroy

int sem_destroy(sem_t *sem);

销毁一个信号量

sem:指向信号量的指针


环形队列

这里的环形队列是基于生产者消费模型实现的

如图所示

如图是一个环形队列,生产者和消费者都是顺时针走,白色代表没有数据,黑色代表有数据, 生产者在消费者前生产,消费者在生产者后消费,这就是生产消费模型的环形队列

根据上文讲述的信号量

我们就可以把环形队列中的资源用信号量划

也就是**_consumer_data=4** _product_data=4

那么也就是说

1.只有_consumer_data>0时,生产者才能生产

2.只有_product_data>0时,消费者才能进行消费

对生产者与消费者之间的关系进行一个总结

消费者与消费者-----------互斥

生产者与生产者-----------互斥

生产者与消费者-----------互斥 无需维护

这里需要单独讲解一下,为什么生产者与消费者之间的互斥是无需维护的呢,

很简单,因为生产者和消费者永远都不会访问到同一块资源!!!

因为在环形队列中,生产者消费者访问同一块数据的时候就是环形队列为全空 ,或者全满的时候

如图:


实现

为了实现环形队列,我们需要用到两个锁,以及用一个vector来维护环形队列,另外在单独标记生产者和消费者的下标

std::vector<T> _ring_queue;

int _cap; // 环形队列的容量上限

// 2. 生产和消费的下标

int _productor_step;

int _consumer_step;

// 3. 定义信号量

sem_t _product_sem; // 生产者关心

sem_t _consumer_sem; // 消费者关心

// 4. 定义锁,维护多生产多消费之间的互斥关系

pthread_mutex_t _productor_mutex;

pthread_mutex_t _consumer_mutex;

入队列(生产行为)

分为三个步骤

1.申请信号量 2.竞争生产锁 3.进行生产

cpp 复制代码
 void Enqueue(const T &in)
    {
        // 生产行为
        P(_room_sem);
        Lock(_productor_mutex);
        // 一定有空间!!!
        _ring_queue[_productor_step++] = in; // 生产
        _productor_step %= _cap;
        Unlock(_productor_mutex);
        V(_data_sem);
    }

这里需要注意的是

进行生产任务的时候,我们只需对_productor_step进行%操作便可以进行环形操作了,这个操作我也在之前的设计循环队列中有所讲解算法题详解:设计循环队列-CSDN博客

出队列(消费行为)

1.申请信号量 2.竞争消费锁 3.进行消费

cpp 复制代码
void Pop(T *out)
    {
        // 消费行为
        P(_data_sem);
        Lock(_consumer_mutex);
        *out = _ring_queue[_consumer_step++];
        _consumer_step %= _cap;
        Unlock(_consumer_mutex);
        V(_room_sem);
    }

与生产行为相似,只需如法炮制便可

代码

cpp 复制代码
#pragma once

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

// 单生产,单消费
// 多生产,多消费
// "321":
// 3: 三种关系
// a: 生产和消费互斥和同步
// b: 生产者之间:
// c: 消费者之间:
// 解决方案:加锁
// 1. 需要几把锁?2把
// 2. 如何加锁?
template<typename T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(int cap): _ring_queue(cap), _cap(cap),  _productor_step(0), _consumer_step(0)
    {
        sem_init(&_product_sem, 0, _cap);
        sem_init(&_consumer_sem, 0, 0);

        pthread_mutex_init(&_productor_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }
    void Enqueue(const T &in)
    {
        // 生产行为
        P(_product_sem);
        Lock(_productor_mutex);
        // 一定有空间!!!
        _ring_queue[_productor_step++] = in; // 生产
        _productor_step %= _cap;
        Unlock(_productor_mutex);
        V(_consumer_sem);
    }
    void Pop(T *out)
    {
        // 消费行为
        P(_product_sem);
        Lock(_consumer_mutex);
        *out = _ring_queue[_consumer_step++];
        _consumer_step %= _cap;
        Unlock(_consumer_mutex);
        V(_consumer_sem);
    }
    ~RingQueue()
    {
        sem_destroy(&_product_sem);
        sem_destroy(&_consumer_sem);

        pthread_mutex_destroy(&_productor_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }
private:
    // 1. 环形队列
    std::vector<T> _ring_queue;
    int _cap; // 环形队列的容量上限

    // 2. 生产和消费的下标
    int _productor_step;
    int _consumer_step;

    // 3. 定义信号量
    sem_t _product_sem; // 生产者关心
    sem_t _consumer_sem; // 消费者关心

    // 4. 定义锁,维护多生产多消费之间的互斥关系
    pthread_mutex_t _productor_mutex;
    pthread_mutex_t _consumer_mutex;
};
相关推荐
深情汤姆21 分钟前
C++ 红黑树
数据结构·c++
davenian1 小时前
<OS 有关> ubuntu 24 不同版本介绍 安装 Vmware tools
linux·ubuntu·vmware
绵绵细雨中的乡音2 小时前
C++第28课-布隆过滤器的介绍
c++·哈希算法
Simulink_2 小时前
ROS学习笔记15——Xacro
linux·笔记·学习·机器人·ros
C++忠实粉丝2 小时前
计算机网络socket编程(5)_TCP网络编程实现echo_server
网络·c++·网络协议·tcp/ip·计算机网络·算法
北京迅为2 小时前
【北京迅为】iTOP-4412全能版使用手册- 第五章 Linux常用命令
linux·嵌入式硬件·4412开发板
南暮思鸢2 小时前
应急响应靶机——linux2
linux·网络安全·wireshark·write up·应急响应靶机系列·宝塔面板利用·webshell流量分析
sun0077003 小时前
ubuntu增加swap交换空间
linux·运维·服务器
胜天半子_王二_王半仙3 小时前
c++源码阅读__smart_ptr__正文阅读
开发语言·c++·开源
程序猿阿伟3 小时前
《C++智能合约与区块链底层交互全解析:构建坚实的去中心化应用桥梁》
c++·区块链·智能合约