C语言-发布订阅模式详解与实践

文章目录

  • C语言发布订阅模式详解与实践
    • 1. 什么是发布订阅模式?
    • 2. 为什么需要发布订阅模式?
    • 3. 实际应用场景
    • 4. 代码实现
      • 4.1 UML 关系图
      • 4.2 头文件 (pubsub.h)
      • 4.3 实现文件 (pubsub.c)
      • 4.4 使用示例 (main.c)
    • 5. 代码分析
      • 5.1 关键设计点
      • 5.2 实现特点
    • 6. 编译和运行
    • 7. 注意事项
    • 8. 改进建议
    • 9. 总结
    • 参考资料

C语言发布订阅模式详解与实践

1. 什么是发布订阅模式?

发布订阅模式定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题。这个主题在状态发生变化时,会通知所有依赖于它的订阅者对象,使它们能够自动更新。

2. 为什么需要发布订阅模式?

  • 实现对象间的松耦合
  • 支持广播通信
  • 动态订阅和取消订阅
  • 事件驱动架构
  • 异步消息处理

3. 实际应用场景

  • 传感器数据分发
  • 消息队列系统
  • 事件处理系统
  • 日志监控
  • 状态更新通知

4. 代码实现

4.1 UML 关系图

Publisher
+publish()
+subscribe()
+unsubscribe()
Subscriber
+update()
Topic
+name
+data

4.2 头文件 (pubsub.h)

#ifndef PUBSUB_H
#define PUBSUB_H#include <stdint.h>
#include <stdbool.h>// 主题数据结构
typedef struct {char name[32];          // 主题名称void* data;            // 主题数据uint32_t data_size;    // 数据大小uint32_t timestamp;    // 时间戳
} Topic;// 订阅者回调函数类型
typedef void (*SubscriberCallback)(const Topic* topic, void* user_data);// 订阅者结构
typedef struct {char name[32];              // 订阅者名称SubscriberCallback callback; // 回调函数void* user_data;            // 用户数据
} Subscriber;// 发布者结构
typedef struct {char name[32];              // 发布者名称Subscriber* subscribers[16]; // 订阅者列表int subscriber_count;       // 订阅者数量
} Publisher;// 创建发布者
Publisher* create_publisher(const char* name);// 销毁发布者
void destroy_publisher(Publisher* publisher);// 订阅主题
bool subscribe(Publisher* publisher, const char* subscriber_name,SubscriberCallback callback,void* user_data);// 取消订阅
bool unsubscribe(Publisher* publisher, const char* subscriber_name);// 发布主题
void publish(Publisher* publisher, const Topic* topic);#endif // PUBSUB_H

4.3 实现文件 (pubsub.c)

#include "pubsub.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>Publisher* create_publisher(const char* name) {Publisher* publisher = (Publisher*)malloc(sizeof(Publisher));strncpy(publisher->name, name, sizeof(publisher->name) - 1);publisher->subscriber_count = 0;memset(publisher->subscribers, 0, sizeof(publisher->subscribers));return publisher;
}void destroy_publisher(Publisher* publisher) {if (!publisher) return;// 释放所有订阅者for (int i = 0; i < publisher->subscriber_count; i++) {free(publisher->subscribers[i]);}free(publisher);
}bool subscribe(Publisher* publisher, const char* subscriber_name,SubscriberCallback callback,void* user_data) {if (!publisher || !subscriber_name || !callback) return false;// 检查是否已达到最大订阅者数量if (publisher->subscriber_count >= 16) {printf("订阅者数量已达上限\n");return false;}// 检查是否已订阅for (int i = 0; i < publisher->subscriber_count; i++) {if (strcmp(publisher->subscribers[i]->name, subscriber_name) == 0) {printf("订阅者 %s 已存在\n", subscriber_name);return false;}}// 创建新订阅者Subscriber* subscriber = (Subscriber*)malloc(sizeof(Subscriber));strncpy(subscriber->name, subscriber_name, sizeof(subscriber->name) - 1);subscriber->callback = callback;subscriber->user_data = user_data;// 添加到订阅者列表publisher->subscribers[publisher->subscriber_count++] = subscriber;printf("订阅者 %s 已添加\n", subscriber_name);return true;
}bool unsubscribe(Publisher* publisher, const char* subscriber_name) {if (!publisher || !subscriber_name) return false;for (int i = 0; i < publisher->subscriber_count; i++) {if (strcmp(publisher->subscribers[i]->name, subscriber_name) == 0) {// 释放订阅者free(publisher->subscribers[i]);// 移动后续订阅者for (int j = i; j < publisher->subscriber_count - 1; j++) {publisher->subscribers[j] = publisher->subscribers[j + 1];}publisher->subscriber_count--;printf("订阅者 %s 已移除\n", subscriber_name);return true;}}printf("未找到订阅者 %s\n", subscriber_name);return false;
}void publish(Publisher* publisher, const Topic* topic) {if (!publisher || !topic) return;printf("\n发布者 %s 发布主题 %s\n", publisher->name, topic->name);// 通知所有订阅者for (int i = 0; i < publisher->subscriber_count; i++) {Subscriber* subscriber = publisher->subscribers[i];printf("通知订阅者 %s\n", subscriber->name);subscriber->callback(topic, subscriber->user_data);}
}

4.4 使用示例 (main.c)

#include "pubsub.h"
#include <stdio.h>// 温度传感器订阅者回调
void temperature_callback(const Topic* topic, void* user_data) {float* threshold = (float*)user_data;float temperature = *(float*)topic->data;printf("温度传感器收到数据: %.1f°C\n", temperature);if (temperature > *threshold) {printf("警告:温度超过阈值 %.1f°C!\n", *threshold);}
}// 日志记录订阅者回调
void logger_callback(const Topic* topic, void* user_data) {printf("日志记录器:主题 %s, 数据大小 %d, 时间戳 %d\n",topic->name, topic->data_size, topic->timestamp);
}int main() {// 创建发布者Publisher* sensor_publisher = create_publisher("传感器发布者");// 创建温度阈值float temp_threshold = 30.0f;// 订阅主题subscribe(sensor_publisher, "温度监控器", temperature_callback, &temp_threshold);subscribe(sensor_publisher, "系统日志", logger_callback, NULL);// 创建并发布温度数据float temp_data[] = {25.5f, 28.3f, 32.7f};for (int i = 0; i < 3; i++) {Topic topic = {.name = "temperature",.data = &temp_data[i],.data_size = sizeof(float),.timestamp = (uint32_t)time(NULL)};publish(sensor_publisher, &topic);}// 取消订阅unsubscribe(sensor_publisher, "系统日志");// 再次发布数据float final_temp = 35.2f;Topic topic = {.name = "temperature",.data = &final_temp,.data_size = sizeof(float),.timestamp = (uint32_t)time(NULL)};publish(sensor_publisher, &topic);// 清理资源destroy_publisher(sensor_publisher);return 0;
}

5. 代码分析

5.1 关键设计点

  1. 发布者管理订阅者列表
  2. 回调机制实现通知
  3. 主题数据封装
  4. 动态订阅管理

5.2 实现特点

  1. 函数指针实现回调
  2. 支持用户数据传递
  3. 订阅者管理完善
  4. 资源管理安全

6. 编译和运行

gcc -c pubsub.c -o pubsub.o
gcc -c main.c -o main.o
gcc pubsub.o main.o -o pubsub_demo

7. 注意事项

  1. 订阅者数量限制
  2. 内存管理安全
  3. 回调函数异常处理
  4. 线程安全考虑

8. 改进建议

  1. 添加主题过滤
  2. 实现异步通知
  3. 支持优先级订阅
  4. 添加订阅者分组

9. 总结

发布订阅模式通过解耦发布者和订阅者,实现了灵活的消息通知机制。这种模式特别适合处理事件驱动的场景。

参考资料

  1. 《设计模式:可复用面向对象软件的基础》
  2. 《C语言程序设计》
  3. 《事件驱动编程》

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

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

相关文章

蓝桥杯2023年第十四届省赛真题-异或和之差

题目来自DOTCPP&#xff1a; 思路&#xff1a; 什么是异或和&#xff1f; ①题目要求我们选择两个不相交的子段&#xff0c;我们可以枚举一个分界线i&#xff0c;子段1在 i 的左边&#xff0c; 子段2在 i 的右边&#xff0c;分别找到子段1和子段2的最大值、最小值。 ②怎么确…

Linux作业2——有关文件系统权限的练习

1、创建/www目录&#xff0c;在/www目录下新建name和https目录&#xff0c;在name和https目录下分别创建一个index.html文件&#xff0c;name下面的index.html文件中包含当前主机的主机名&#xff0c;https目录下的index.html文件中包含当前主机的ip地址。 #创建/www目录&…

leeCode 70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 阶 示例 2&#x…

算法题(105):小猫爬山

审题&#xff1a; 本题需要我们找出将n个小猫放在有限重的缆车上运下山所需的最小缆车数 时间复杂度分析&#xff1a;本题的数据量小于等于18&#xff0c;所以我们在做好剪枝的前提下可以使用深度优先搜索解题 思路&#xff1a; 方法一&#xff1a;dfs 搜索策略&#xff1a;将小…

第十六章:Specialization and Overloading_《C++ Templates》notes

Specialization and Overloading 一、模板特化与重载的核心概念二、代码实战与测试用例三、关键知识点总结四、进阶技巧五、实践建议多选题设计题代码测试说明 一、模板特化与重载的核心概念 函数模板重载 (Function Template Overloading) // 基础模板 template<typename…

多协议兼容+高并发处理:EasyCVR如何破解AI安防规模化落地难题?

随着AI技术在安防领域的深入应用&#xff0c;规模化部署面临两大核心挑战&#xff1a;设备协议碎片化导致的接入壁垒与海量视频流并发带来的性能瓶颈。TSINGSEE青犀视频的EasyCVR平台通过“多协议兼容高并发处理”双引擎驱动&#xff0c;结合云边端协同架构与智能算法优化&…

IntelliJ IDEA 中 Git 高频问题与操作详解|新手避坑指南

标签&#xff1a;IntelliJ IDEA Git操作, Git教程, 版本控制, 冲突解决, 分支管理 引言 你是否遇到过这些问题&#xff1f; 代码提交后想撤销怎么办&#xff1f;合并分支时冲突不会解决&#xff1f;不小心把错误代码推送到远程仓库&#xff1f; 本文针对 IntelliJ IDEA 中 Git …

【聊聊层次式架构设计:像搭乐高一样构建软件大厦】

文章目录 聊聊层次式架构设计&#xff1a;像搭乐高一样构建软件大厦理论篇&#xff1a;层次式架构的“千层套路”最底层&#xff1a;基础设施层——默默付出的“基石侠”数据访问层&#xff1a;“数据快递员”业务逻辑层&#xff1a;智慧的“大脑中枢”表示层&#xff1a;软件的…

N列股票收盘价为起点的马科维茨(Markowitz)均值—方差理论

1. 数据准备与收益率计算 输入数据&#xff1a; 假设你有一个矩阵&#xff0c;每一列代表一只股票的历史收盘价序列。每一行对应一个时间点的收盘价。 计算收益率&#xff1a; 马科维茨理论要求使用资产的收益率而非价格。常用的收益率计算方法有对数收益率或简单收益率。 2.…

Conda常用命令汇总(持续更新中)

原文章&#xff1a;安装和使用Miniconda来管理Python环境-CSDN博客 一、Miniconda的使用 Miniconda没有GUI界面&#xff0c;只能通过conda命令对Python环境和软件包进行管理&#xff0c;所以这里主要介绍一下conda的常用命令。 1. Conda相关 (1)查询conda版本 conda --vers…

Redis Cluster 详解

Redis Cluster 详解 1. 为什么需要 Redis Cluster&#xff1f; Redis 作为一个高性能的内存数据库&#xff0c;在单机模式下可能会遇到以下问题&#xff1a; 单机容量受限&#xff1a;Redis 是基于内存存储的&#xff0c;单机的内存资源有限&#xff0c;单实例的 Redis 只能…

利用 MATLAB/Simulink 建立完整的控制系统模型,并进行阶跃响应和负载扰动响应仿真

-利用 MATLAB/Simulink 建立完整的控制系统模型,包括单一控制回路(电流、速度、位置)和整个系统的级联模型 仿真任务包括验证各回路的阶跃响应、负载扰动响应等,确保系统在动态性能上满足设计要求。 以下是在MATLAB/Simulink中建立完整控制系统模型(包含单一控制回路和级联…

python基于spark的心脏病患分类及可视化(源码+lw+部署文档+讲解),源码可白嫖!

摘要 时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;汽车数据分析平台当然不能排除在外。本次我所开发的心脏病患分类及可视化系统是在实际应用和软件工程的开发原理之上&#xff0c;运用Pyth…

3.milvus索引-HNSW

索引作用 加速大型数据集上的查询。 向量字段&#xff0c;仅只能创建一个索引。 milvus支持的向量索引类型大部分使用 近似最近邻搜索算法。ANNS该算法的核心不局限于返回最准确的结果&#xff0c;而是仅搜索目标的邻居。ANNS通过在可接受的范围内牺牲准确性提高检索效率。 …

Python(学习二)

列表&#xff1a;[] 列表是可以容纳不同类型的数据的 列表取&#xff1a; 列表切片&#xff1a;一次去获取多个元素 第三个参数&#xff0c;设置跨度值&#xff1a; 列表倒序输出 列表增&#xff1a; 列表后面添加元素&#xff1a; 切片&#xff1a;实现添加元素 任意位置…

【中文翻译】第1章-The Algorithmic Foundations of Differential Privacy

为方便阅读&#xff0c;故将《The Algorithmic Foundations of Differential Privacy》翻译项目内容搬运至此&#xff1b; 教材原文地址&#xff1a;https://www.cis.upenn.edu/~aaroth/Papers/privacybook.pdf 中文翻译版 Github 项目地址1&#xff1a;https://github.com/gu…

UI-TARS与Midscene.js自动化探索

结合 Midscene.js 和 UI-TARS 大模型 实现 UI 页面自动化的可实施方案&#xff0c;涵盖环境配置、核心流程、代码示例及优化建议&#xff1a; 一、环境配置与工具集成 安装 Midscene.js 方式一&#xff1a;通过 Chrome 插件快速安装&#xff08;适用于浏览器自动化场景&#x…

Web开发-JS应用NodeJS原型链污染文件系统Express模块数据库通讯

知识点&#xff1a; 1、安全开发-NodeJS-开发环境&功能实现 2、安全开发-NodeJS-安全漏洞&案例分析 3、安全开发-NodeJS-特有漏洞 node.js就是专门运行javascript的一个应用程序&#xff0c;区别于以往用浏览器解析原生js代码&#xff0c;node.js本身就可以解析执行js代…

Spring AOP 核心概念与实践指南

第一章&#xff1a;AOP 核心概念与基础应用 1.1 AOP 核心思想 ​面向切面编程&#xff1a;通过横向抽取机制解决代码重复问题&#xff08;如日志、事务、安全等&#xff09;​核心优势&#xff1a;不修改源代码增强功能&#xff0c;提高代码复用性和可维护性 1.2 基础环境搭…

Flutter使用自签证书打包ipa

在 Flutter 中使用自签证书打包 IPA 文件&#xff0c;可以通过以下步骤完成&#xff1a; 1. 准备自签证书 方式一 生成自签证书&#xff1a; 打开 钥匙串访问 应用。选择 证书助理 > 创建证书。按照提示填写证书信息&#xff0c;选择证书类型为 代码签名&#xff0c;并保存…