基于建造者模式的信号量与理解建造者模式

信号量是什么?

AI解释:信号量(Semaphore)是操作系统中用于 进程同步与互斥 的经典工具,由荷兰计算机科学家 Edsger Dijkstra 在 1965 年提出。它本质上是一个 非负整数变量,通过原子操作(P 操作和 V 操作)实现对共享资源的访问控制。

System-V版本的信号量相关API:

Ftok

函数定义:
key_t ftok(const char *pathname, int proj_id);
函数作用:

获取唯一的key值标识符。

参数解释:
  • 传入一个有效的文件路径和一个 <255 的整数数字,返回一个具有唯一性的 key

semget

函数定义:
int semget(key_t key, int nsems, int semflg);
函数作用:

获取或者创建信号量集的文件描述符 fd

参数解释:
  • keyftok 调用成功返回的 key
  • nsems:要创建/获取的信号量集合中的信号量数量 cnt
  • semflg
    • 传入 (IPC_CREATE | IPC_EXCL | 文件权限) 表示创建信号量集合,并返回 fd
    • 传入 IPC_CREATE 表示获取指定的信号量集合 fd

semctl

函数定义:
int semctl(int semid, int semnum, int cmd, ...);
函数作用:

控制指定的信号量集合,删除或者修改。

参数解释:
  • semid:指定的信号量集的文件描述符 fd
  • semnum:要控制的信号量集合中的下标(数组下标从0开始)。
  • cmd
    • 设置为 IPC_RMID 时,表示删除指定信号量集,可忽略 semnum(设为0)和可变参数。
    • 设置为 SETVAL 时,表示设置信号量的值,需手动创建联合体 union semun 并设置 val 字段:
      union semun {int val;               /* Value for SETVAL */struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO */
      } sem_un;
      sem_un.val = val; // 设置val字段
      semctl(fd, i, SETVAL, sem_un); // 传入可变参数
      

semop

函数定义:
int semop(int semid, struct sembuf *sops, unsigned nsops);
函数作用:

对信号量进行 PV 操作。

PV操作:

使用系统提供的 struct sembuf 结构体:

struct sembuf {unsigned short sem_num;  /* 信号量下标 */short          sem_op;   /* 操作值(-1为P操作,1为V操作) */short          sem_flg;  /* 标志,一般设为SEM_UNDO */
};
参数解释:
  • sem_num:信号量集合中信号量的下标。
  • sem_op-1 表示 P 操作,1 表示 V 操作(本质是对信号量值进行加减)。
  • sem_flg:一般设置为 SEM_UNDO,表示异常时销毁信号量。
  • semid:信号量集描述符。
  • sopsstruct sembuf 数组地址,支持批量 PV 操作。
  • nsops:数组大小。

实现基于简单建造者模式的信号量封装

日志模块 gitee:https://gitee.com/LOG_C/log

#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include "Log.hpp"
using namespace ns_log;#define CREATE_SEM (IPC_CREAT | IPC_EXCL | 0666)
#define GET_SEM (IPC_CREAT)
#define P_OP (-1) /* P操作 */
#define V_OP (1)  /* V操作 */
const std::string SEM_PATH = "../tmp"; // 需要先创建有效目录
const int proj_id = 123;
const int default_sem_nums = 1;std::string ToHex(int num) {char buf[64];sprintf(buf, "0x%x", num);return buf;
}class Semaphore {
private:int _fd;void PV(int op) {struct sembuf sem_buf;sem_buf.sem_num = 0;sem_buf.sem_op = op;sem_buf.sem_flg = SEM_UNDO;int n = semop(_fd, &sem_buf, 1);if (n < 0) {LOG(DEBUG, "op = %d失败\n", op);return;}}public:Semaphore(int fd) : _fd(fd) {}void P() { PV(P_OP); LOG(DEBUG, "P操作done\n"); }void V() { PV(V_OP); LOG(DEBUG, "V操作done\n"); }~Semaphore() {if (_fd > 0) {int n = semctl(_fd, 0, IPC_RMID);if (n < 0) {LOG(ERROR, "semctl的IPC_RMID操作异常!,异常信息:%s\n", strerror(errno));}LOG(DEBUG, "销毁信号量done\n");}}
};
using SemPtr = std::shared_ptr<Semaphore>;class SemaphoreBuilder {
private:int _val;bool Init(int fd, int num, int val) {union semun {int val;struct semid_ds *buf;unsigned short *array;struct seminfo *__buf;} sem_un;sem_un.val = val;for (int i = 0; i < num; i++) {int ret = semctl(fd, i, SETVAL, sem_un);if (ret < 0) {LOG(ERROR, "semctl的SETVAL操作异常!,异常信息:%s\n", strerror(errno));return false;}}return true;}public:SemaphoreBuilder() {}SemaphoreBuilder& SetVal(int val) { _val = val; return *this; }SemPtr Build(int flag, int num = default_sem_nums) {LOG(DEBUG, "开始build信号量\n");key_t key = ftok(SEM_PATH.c_str(), proj_id);if (key == -1) {LOG(ERROR, "ftok操作异常!,异常信息:%s\n", strerror(errno));return nullptr;}LOG(INFO, "frok的key为:%s\n", ToHex(key).c_str());int sem_fd = semget(key, num, flag);if (sem_fd == -1) {LOG(ERROR, "semget操作异常!,异常信息:%s\n", strerror(errno));return nullptr;}if (flag == CREATE_SEM) {Init(sem_fd, num, _val);}return std::make_shared<Semaphore>(sem_fd);}
};

理解建造者模式

定义:

将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。通过分解复杂对象的构建步骤,每一步创建一部分,最后组合成完整对象。

角色:

  1. 产品(Product)
    • 被构建的复杂对象,包含多个组成部分(属性和行为)。
  2. 抽象建造者(Builder)
    • 定义构建复杂对象各部分的接口,以及返回最终产品的方法。
  3. 具体建造者(Concrete Builder)
    • 实现抽象建造者接口,具体构建对象的各部分,生成不同类型或形式的产品。
  4. 指挥者(Director)
    • 负责调用具体建造者构建对象,不依赖具体类,仅通过建造者接口操作。

重构后的建造者模式(信号量场景)

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include "Log.hpp"
using namespace ns_log;#define CREATE_SEM (IPC_CREAT | IPC_EXCL | 0666)
#define GET_SEM (IPC_CREAT)
#define P_OP (-1) /* P操作 */
#define V_OP (1)  /* V操作 */
const std::string SEM_PATH = "../tmp";
const int proj_id = 123;
const int default_sem_nums = 1;enum { FTOK_ERROR = 1, SEMGET_ERROR, SEMCTL_ERROR };std::string ToHex(int num) {char buf[64];sprintf(buf, "0x%x", num);return buf;
}// 产品:信号量
class Semaphore {
private:int _fd;void PV(int who, int op) {struct sembuf sem_buf;sem_buf.sem_num = who;sem_buf.sem_op = op;sem_buf.sem_flg = SEM_UNDO;int n = semop(_fd, &sem_buf, 1);if (n < 0) {LOG(DEBUG, "op = %d失败\n", op);return;}}public:Semaphore(int fd) : _fd(fd) {}void P(int who) { PV(who, P_OP); LOG(DEBUG, "P操作done\n"); }void V(int who) { PV(who, V_OP); LOG(DEBUG, "V操作done\n"); }int Fd() const { return _fd; }~Semaphore() {if (_fd > 0) {int n = semctl(_fd, 0, IPC_RMID);if (n < 0) {LOG(ERROR, "semctl的IPC_RMID操作异常!,异常信息:%s\n", strerror(errno));}LOG(DEBUG, "销毁信号量done\n");}}
};// 抽象建造者
class Builder {
public:virtual ~Builder() {}virtual void BuildKey() = 0;         // 获取键值virtual void SetPerm(int perm) = 0;  // 设置权限virtual void SetSemNum(int num) = 0; // 设置信号量数量virtual void SetVal(const std::vector<int>& init_vals) = 0; // 初始化值virtual void Init() = 0;             // 初始化信号量virtual void Build(int flag) = 0;     // 构建信号量
};// 具体建造者
class SemaphoreBuilder : public Builder {
private:bool init(int fd, int index, int val) {union semun {int val;struct semid_ds *buf;unsigned short *array;struct seminfo *__buf;} sem_un;sem_un.val = val;int ret = semctl(fd, index, SETVAL, sem_un);if (ret < 0) {LOG(ERROR, "semctl的SETVAL操作异常!,异常信息:%s\n", strerror(errno));return false;}return true;}public:std::shared_ptr<Semaphore> GetSemaphore() { return _sem; }void BuildKey() override {_key = ftok(SEM_PATH.c_str(), proj_id);if (_key == -1) {LOG(ERROR, "ftok操作异常!,异常信息:%s\n", strerror(errno));exit(FTOK_ERROR);}LOG(INFO, "frok的key为:%s\n", ToHex(_key).c_str());}void SetPerm(int perm) override { _perm = perm; }void SetSemNum(int num) override { _sem_cnts = num; }void SetVal(const std::vector<int>& init_vals) override { _init_vals = init_vals; }void Init() override {if (_sem_cnts > 0 && _sem_cnts == _init_vals.size()) {for (int i = 0; i < _sem_cnts; i++) {if (!init(_sem->Fd(), i, _init_vals[i])) {exit(SEMCTL_ERROR);}}}}void Build(int flag) override {int sem_fd = semget(_key, _sem_cnts, flag);if (sem_fd == -1) {LOG(ERROR, "semget操作异常!,异常信息:%s\n", strerror(errno));exit(SEMGET_ERROR);}_sem = std::make_shared<Semaphore>(sem_fd);}private:std::shared_ptr<Semaphore> _sem;key_t _key;int _perm;int _sem_cnts;std::vector<int> _init_vals;
};// 指挥者
class Director {
public:void construct(std::shared_ptr<SemaphoreBuilder>& sem_builder,int flag,int num,const std::vector<int>& vals,int perm) {sem_builder->BuildKey();sem_builder->SetPerm(perm);sem_builder->SetVal(vals);sem_builder->SetSemNum(num);sem_builder->Build(flag);if (flag == CREATE_SEM) {sem_builder->Init();}}
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开闭原则(OCP)

非常棒的问题&#xff01;&#x1f50d; 开闭原则&#xff08;OCP, Open/Closed Principle&#xff09;是软件设计的核心原则之一&#xff0c;下面我将从定义、意义、优劣分析、Python示例和结构图五个方面完整解析给你。 &#x1f9e0; 什么是开闭原则&#xff1f; 开闭原则&a…

python数据分析(七):Pandas 数据变形与重塑

Pandas 数据变形与重塑全面指南 1. 引言 在数据分析过程中&#xff0c;我们经常需要将数据从一种结构转换为另一种结构&#xff0c;以适应不同的分析需求。Pandas 提供了丰富的数据变形与重塑功能&#xff0c;包括旋转(pivot)、堆叠(stack)、融合(melt)等多种操作。本文将详细…

Android学习总结之jetpack组件间的联系

在传统安卓开发中&#xff0c;UI 组件&#xff08;Activity/Fragment&#xff09;常面临三个核心问题&#xff1a; 生命周期混乱&#xff1a;手动管理 UI 与数据的绑定 / 解绑&#xff0c;易导致内存泄漏&#xff08;如 Activity 销毁后回调仍在触发&#xff09;。数据断层&am…

C++初阶:类和对象(二)

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 一.运算符重载 1.1 基本概念 定义 参数规则 特性 选择原则 重载要点 二.类的默认成员函数 2.1 构造函数 构造函数的特点 2.2 析构函数 析构函数的特点 2.3 拷贝构造函数 拷贝构造的特点 2.4 拷贝赋值运算符重…

【c++】【STL】priority_queue详解

目录 priority_queue的作用priority_queue的接口构造函数emptysizetoppushpopswap priority_queue的实现仿函数&#xff08;函数对象&#xff09;是什么&#xff1f;向上调整算法&#xff08;adjustup&#xff09;向下调整算法&#xff08;adjustdown&#xff09;迭代器构造pus…

测试——用例篇

目录 1. 测试用例 1.1 概念 2. 设计测试用例的万能公式 2.1 常规思考逆向思维发散性思维 2.2 万能公式 3. 设计测试用例例的方法 3.1 基于需求的设计方法 ​编辑 3.2 具体的设计方法 3.2.1 等价类 3.2.2 边界值 3.2.3 正交法 3.2.4 判定表法 3.2.5 场景法 3.2.6…

销售总监求职简历模板

模板信息 简历范文名称&#xff1a;销售总监求职简历模板&#xff0c;所属行业&#xff1a;其他 | 职位&#xff0c;模板编号&#xff1a;KREUNY 专业的个人简历模板&#xff0c;逻辑清晰&#xff0c;排版简洁美观&#xff0c;让你的个人简历显得更专业&#xff0c;找到好工作…

AE脚本 关键帧缓入缓出曲线调节工具 Flow v1.5.0 Win/Mac

Flow是一个非常好用的After Effects脚本,它可以让你更加轻松自如地调整关键帧的速度曲线,无需触碰老旧复杂的图形编辑器。 AE脚本介绍 Flow为After Effects带来了一个简洁的界面,使自定义动画曲线变得十分容易,无需深入研究速度和影响力这些让人困惑的概念 - 只需绘制一个曲线…

ACGRIME:用于全局优化和特征选择的自适应混沌高斯RIME优化器,附完整版免费代码

自然现象中&#xff0c;软冰的形成过程由 Set al. [42] 提出&#xff0c;软冰是空气中的过冷水滴在接触固体物体并冻结时形成的。这种现象发生在特定的气候条件下&#xff0c;当水蒸气尚未凝结时&#xff0c;导致冰覆盖的表面呈现出独特的树枝状和叶子状景观。它在软冰的生长和…

大模型开发学习笔记

文章目录 大模型基础大模型的使用大模型训练的阶段大模型的特点及分类大模型的工作流程分词化(tokenization)与词表映射 大模型的应用 进阶agent的组成和概念planning规划子任务分解ReAct框架 memory记忆Tools工具\工具集的使用langchain认知框架ReAct框架plan-and-Execute计划…

4.27-5.4学习周报

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract一、方法介绍2.Rainbow Memory(RM)2.1多样性感知内存更新2.2通过数据增强增强样本多样性(DA) 二、使用步骤1.实验概况2.RM核心代码 总结 摘要 本博客概…

AI Rack架构高速互连的挑战:损耗设计与信号完整性的设计框架

在AI驱动的时代&#xff0c;系统设计已经从单一PCB的视角&#xff0c;逐步转向以整个rack为单位来考量。 对于信号完整性而言&#xff0c;焦点以不再局限于单一PCB上的损耗&#xff0c;而是扩展到芯片与芯片之间的端到端互连损耗&#xff08;end-to-end interconnect loss&…

杭电oj(1180、1181)题解

目录 1180 题目 思路 问题概述 代码思路分析 1. 数据结构与全局变量 2. BFS 函数 bfs 3. 主函数 main 总结 代码 1181 题目 思路 1. 全局变量的定义 2. 深度优先搜索函数 dfs 3. 主函数 main 总结 代码 1180 题目 思路 注&#xff1a;当走的方向和楼梯方向一…

软件测试概念

这里写目录标题 需求开发模型软件生命周期瀑布模型螺旋模型增量模型、迭代模型敏捷模型Scrum 测试模型V模型W模型&#xff08;双V模型&#xff09; 需求 用户需求&#xff1a;没有经过合理的评估&#xff0c;通常就是一句话 软件需求&#xff1a;是开发人员和测试人员执行工作…

数字基带信号和频带信号的区别解析

数字基带信号和数字频带信号是通信系统中两种不同的信号形式&#xff0c;它们的核心区别在于是否经过调制以及适用的传输场景。以下是两者的主要区别和分析&#xff1a; 1. 定义与核心区别 数字基带信号&#xff08;Digital Baseband Signal&#xff09; 未经调制的原始数字信号…

Linux52 运行百度网盘 解决故障无法访问repo nosandbox 未解决:疑似libstdc++版本低导致无法运行baidu网盘

昨日参考 哦 我是root Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64 没了 计划去手动下一个 还是不行 放弃 猜测是 centos7 过期了 一些依赖组件也没地方下载了 通过阿里云镜像站下载 之前安装的好像不是这个版本 还是计划用yum去下载依赖&#xff0c;先处…

2000-2022年上市公司数字经济专利申请数据

2000-2022年上市公司数字经济专利申请数据 1、时间&#xff1a;2000-2022年 2、来源&#xff1a;国家知识产权局 3、指标&#xff1a;年份、股票代码、股票简称、行业名称、行业代码、省份、城市、区县、行政区划代码、城市代码、区县代码、首次上市年份、上市状态、数字经济…

机器学习之五:基于解释的学习

正如人们有各种各样的学习方法一样&#xff0c;机器学习也有多种学习方法。若按学习时所用的方法进行分类&#xff0c;则机器学习可分为机械式学习、指导式学习、示例学习、类比学习、解释学习等。这是温斯顿在1977年提出的一种分类方法。 有关机器学习的基本概念&#xff0c;…

Chromium 134 编译指南 - Android 篇:安装构建依赖项(七)

1. 引言 欢迎来到《Chromium 134 编译指南》系列的第七篇文章&#xff01;在前面的章节中&#xff0c;我们已经成功获取了Chromium源代码&#xff0c;并将其配置为支持Android平台。这些步骤为我们的编译之旅奠定了坚实的基础&#xff0c;但在开始实际编译之前&#xff0c;我们…

java 进阶 1.0

静态方法 static 就是能直接用&#xff0c;不用再new一个对象了 一般java中Math等静态类就是可以直接使用其方法 main函数里面不能包含太多的逻辑性语句&#xff0c;全部写成模块 写好程序之后如何测试呢&#xff1f; 使用junit&#xff0c;不能在main函数里测试 测试本身就…