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.进行生产

 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.进行消费

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

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

代码

#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;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/721189.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leaflet地图实例

ReactTypeScript实例&#xff1a; import React, { useEffect, useRef } from "react"; import * as L from "leaflet"; import "leaflet/dist/leaflet.css";const App: React.FC () > {const mapRef useRef<HTMLDivElement>(null);…

TestProject Python SDK入门

2024软件测试面试刷题&#xff0c;这个小程序&#xff08;永久刷题&#xff09;&#xff0c;靠它快速找到工作了&#xff01;&#xff08;刷题APP的天花板&#xff09;-CSDN博客跳槽涨薪的朋友们有福了&#xff0c;今天给大家推荐一个软件测试面试的刷题小程序。​编辑https://…

达梦导入导出

针对导出数据库表结构通常有 3 种方法&#xff1a; 使用 DTS 导出 打开 DTS 迁移工具&#xff0c;选择【DM-->SQL】并链接到数据库中&#xff0c;如下图所示&#xff1a; 添加定义脚本&#xff0c;并选择【迁移范围】&#xff08;仅迁移对象定义&#xff09;&#xff0c;如…

后端开发中缓存的作用以及基于Spring框架演示实现缓存

缓存的作用及演示 现在我们使用的程序都是通过去数据库里拿数据然后展示的 长期对数据库进行数据访问 这样数据库的压力会越来越大 数据库扛不住了 创建了一个新的区域 程序访问去缓存 缓存区数据库 缓存里放数据 有效降低数据访问的压力 我们首先进行一个演示 为了演示…

MySQL事务、数据库的存储引擎

1. 事务的概念 定义&#xff1a;事务就是一组数据库操作序列&#xff08;包含一个或多个SQL操作命令&#xff09;&#xff0c;事务会把所有操作看作是一个不可分割的整体向数据库系统提交或撤销操作&#xff0c;所有操作要么都执行&#xff0c;要么都不执行。 1.1事务的 ACID …

嵌入式学习记录6.13(qt day1)

一.思维导图 二.练习&#xff08;简单模拟tim界面&#xff09; 2.1代码 mywidget.cpp #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {this->setWindowTitle("Tim");this->setWindowIcon(QIcon("C:\\Users\\zy\…

Vue61-消息订阅与发布-任意组件之间的通信

一、原理图 原生的JS不能实现订阅与发布&#xff0c;要借助第三方库&#xff1a;pubsub-js&#xff08;任何一个框架都能用&#xff01;&#xff09; 二、案例实现 school组件&#xff0c;需要数据&#xff08;订阅消息&#xff09;&#xff0c;student组件提供数据&#xff0…

现代互联网应用中的HTTPS技术

现代互联网应用中的HTTPS技术 在现代互联网应用中&#xff0c;网上诈骗、垃圾邮件和数据泄露等现象时有发生。为了保障数据安全&#xff0c;我们通常选择采用HTTPS技术。甚至在iOS开发中&#xff0c;调用接口必须是HTTPS接口。现在&#xff0c;部分浏览器也开始强制要求网站必…

这个开源软件,送给爱读书的你!!【送源码】

对于喜欢阅读的人来说&#xff0c;一定经历过从一本厚厚的修仙书籍到MP3、MP4的阅读时代&#xff0c;再到现今的手机软件。 但是现在的阅读软件经常会遇见以下问题&#xff1a;比如广告弹窗太多&#xff0c;排版乱&#xff0c;甚至很多的APP都进入会员时代&#xff0c;再加上一…

【RK3588/算能/Nvidia智能盒子】挑战「无电无网」部署AI算法,守护大亚湾荃美石化码头工地安全

“万顷碧波之上&#xff0c;一座千米钢栈桥如蛟龙出水&#xff0c;向大海蜿蜒。钢栈桥上的项目建设者正在加紧作业&#xff0c;为助推惠州大亚湾加快建设成为世界级绿色石化基地全力奋战。”这是不久前北京日报对大亚湾惠州港荃湾港区荃美石化码头工地的描述。 △ 图片来源于北…

C#——文件读取FileStream类详情

文件读取FileStream类 一个文件进行读写的时候&#xff0c;会变成一个文件流 FileStream类输入流 用于从文件进行读取文件。输出流&#xff0c;向文件写入的操作 FilleStream用于文件当中任何位置的读写 此文章借鉴与&#xff1a;C#教程&#xff08;非常详细&#xff09; Fil…

基于minhook的Windows HOOK

MinHook是一个基于微软Detours技术的可移植Hook库&#xff0c;它允许开发者在运行时更改函数定义&#xff0c;而无需修改原始函数代码。以下是关于MinHook的详细介绍&#xff1a; 基本概念 定义&#xff1a;MinHook使用内存污染和跳转技术来实现Hook&#xff0c;使得开发者能…

2024 年值得关注的 9 个最佳开源大语言模型

一、简述 开源模型在提供创新方面发挥着至关重要的作用&#xff0c;同时为开发人员、爱好者和开发人员提供了深入研究其复杂之处并对其进行微调以执行特定任务的机会。 我们将探讨一些正在开拓新市场前景并带来其独特能力和优势的顶级开源 LLM。 二、开源模型清单 1.GPT-NeoX…

【AICFD教程】汽车外气动仿真,小白学CFD的入门案例

【视频教程】 【教程】汽车外气动仿真&#xff0c;小白学CFD的入门案例 【文字教程】 1. 案例背景 1.1 学习目标 本案例针对某汽车仿真模型&#xff0c;在车速为40m/s时进行了汽车外流场的数值模拟。 本案例教程旨在演示AICFD中以下场景与功能的操作&#xff1a; a. 单域外…

【Linux Vim的保姆级教程】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

统计学一(术语,正态)

目录 一&#xff0c;常用术语 二&#xff0c;正态分布&#xff08;Normal Distribution&#xff09; 三&#xff0c;中心极限定理(Central Limit Theorem) 一&#xff0c;常用术语 population(族群)&#xff1a;要统计的总的 populationSize(族群数量)&#xff1a;要统计的总…

CleanMyMac X for Mac系统优化垃圾清理软件卸载 工具(小白轻松上手,简单易学)

Mac分享吧 文章目录 效果一、准备工作二、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、启动台显示软件图标&#xff0c;表示安装成功 三、运行测试1、打开软件&#xff0c;配置2、授权&#xff0c;允许完全磁盘访问 安装完成&a…

【Spring Cloud应用框架】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

东莞酷得:电子玩具嵌入式方案商

东莞市酷得智能科技有限公司&#xff0c;作为一家专业的玩具底层方案服务商&#xff0c;与国内外多家优秀制造企业有着深度合作&#xff0c;始终坚持以孩子为中心&#xff0c;以创新为动力&#xff0c;为孩子们打造独具特色的玩具产品。公司拥有一支专业的设计团队&#xff0c;…

南阳理工学院(期末)算法分析练习题

一、算法阅读分析题&#xff1a; 1.分析如下算法&#xff0c;回答问题&#xff08;10分&#xff09;。 该算法的作用是什么(2分)&#xff1f;分析该算法的时间复杂度(5分)?设计算法的一个输入&#xff0c;并给出对应的算法输出结果(3分) &#xff08;1&#xff09;该算法的作…