0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

观察者模式在 RT-Thread 中的实现 | 技术集结

RT-Thread官方账号 2026-05-16 10:22 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

嵌入式系统中,模块间的高耦合和轮询机制往往带来资源浪费与响应延迟。观察者模式通过“发布-订阅”机制,能够以轻量级方式实现模块解耦与事件驱动。本文结合传感器更新、系统状态变化等典型场景,对比事件通知机制,并给出C语言代码实现,帮助开发者高效应用观察者模式。

目录


为啥要使用观察者模式


场景举例


观察者模式在嵌入式中的核心优点


观察者模式和事件通知对比


观察者模式代码实现

1 为啥要使用观察者模式

为了解决嵌入式系统中常见的模块解耦、事件驱动、低功耗响应 等核心问题。虽然 C 语言没有类和继承,但通过函数指针和结构体,完全可以轻量级实现观察者模式

2 场景举例

传感器数据更新

温度传感器每 500ms 采样一次

多个模块关心这个值:LCD 显示、日志记录、云端上报、过温告警

若每个模块都轮询读取 → 浪费 CPU、代码耦合高

用观察者模式:传感器“发布”数据,各模块“订阅” →事件驱动、按需响应

系统状态变化通知

网络连接状态(断开/重连)

电池电量低

OTA 升级完成

多个业务模块需做出反应(如 UI 刷新、保存上下文、关闭外设)

硬件中断与业务逻辑分离

按键中断触发 → 不能在 ISR 中做复杂处理

通过观察者通知应用层任务 →安全、可扩展

3 观察者模式在嵌入式中的核心优点

24b3f84a-50ce-11f1-ab55-92fbcf53809c.png

4 观察者模式和事件通知对比

4.1 功能特性对比

24daa40e-50ce-11f1-ab55-92fbcf53809c.png

4.2 使用场景分析

4.2.1 观察者模式适用场景

// 1. 传感器数据变化通知多个处理模块sensor_subject.notify(sensor_data);// 2. 系统状态变化广播system_state_subject.notify(new_state);// 3. 配置参数更新通知config_subject.notify(changed_params);

4.2.2 事件通知适用场景

// 1. 线程间同步等待特定条件rt_event_recv(event, EVENT_DATA_READY, RT_EVENT_FLAG_AND, timeout, &recved);// 2. 多个事件源的组合条件等待rt_event_recv(event, EVENT_A | EVENT_B, RT_EVENT_FLAG_OR, timeout, &recved);// 3. 异步任务完成通知rt_event_send(task_event, TASK_COMPLETED);

4.3 性能特点对比

观察者模式优势:

即时通知:无需等待,直接回调执行

数据丰富:可传递复杂数据结构指针

解耦性好:观察者与被观察者松耦合

扩展性强:易于添加新的观察者类型

事件通知优势:

标准IPC:符合RT-Thread IPC规范

阻塞等待:支持超时和非阻塞模式

条件组合:AND/OR逻辑组合等待

系统集成:与其他IPC机制统一管理

4.4 选择建议

选择观察者模式当:

需要立即响应状态变化

要传递复杂的数据结构

构建松耦合的组件架构

实现发布-订阅模式

选择事件通知当:

需要线程间同步等待

要求阻塞或超时机制

处理多种事件源的组合条件

利用RT-Thread标准IPC机制

5 观察者模式代码实现

observer.c

/*** @file observer.c* @brief 观察者模式实现文件* @author* @version 1.0* @date 2026-02-10** 基于RT-Thread rt_list实现的观察者模式具体实现*/#include"observer.h"#defineDBG_TAG "OBSERVER"#defineDBG_LVL DBG_INFO#include/*** @brief 初始化被观察者*/rt_err_tsubject_init(subject_t*subject){ RT_ASSERT(subject != RT_NULL); /* 初始化观察者链表 */ rt_list_init(&subject->observer_list); /* 运行时初始化自旋锁 */ rt_spin_lock_init(&subject->lock); LOG_D("Subject initialized"); returnRT_EOK;}/*** @brief 添加观察者*/rt_err_tsubject_add_observer(subject_t*subject,observer_t*observer){ rt_base_tlevel; RT_ASSERT(subject != RT_NULL); RT_ASSERT(observer != RT_NULL); RT_ASSERT(observer->update != RT_NULL); /* 关闭中断并获取自旋锁 */ level =rt_spin_lock_irqsave(&subject->lock); /* 检查观察者是否已经存在 */ rt_list_t*node; rt_list_for_each(node, &subject->observer_list) { observer_t*existing_observer =rt_list_entry(node,observer_t, list); if(existing_observer == observer) { rt_spin_unlock_irqrestore(&subject->lock, level); LOG_W("Observer already exists"); return-RT_ERROR; } } /* 添加观察者到链表尾部 */ rt_list_insert_before(&subject->observer_list, &observer->list); /* 恢复中断并释放自旋锁 */ rt_spin_unlock_irqrestore(&subject->lock, level); LOG_D("Observer added"); returnRT_EOK;}/*** @brief 移除观察者*/rt_err_tsubject_remove_observer(subject_t*subject,observer_t*observer){ rt_base_tlevel; rt_err_tresult = -RT_ERROR; RT_ASSERT(subject != RT_NULL); RT_ASSERT(observer != RT_NULL); /* 关闭中断并获取自旋锁 */ level =rt_spin_lock_irqsave(&subject->lock); /* 在链表中查找并移除观察者 */ rt_list_t*node, *temp; rt_list_for_each_safe(node, temp, &subject->observer_list) { observer_t*existing_observer =rt_list_entry(node,observer_t, list); if(existing_observer == observer) { rt_list_remove(node); result = RT_EOK; break; } } /* 恢复中断并释放自旋锁 */ rt_spin_unlock_irqrestore(&subject->lock, level); if(result == RT_EOK) { LOG_D("Observer removed"); } else { LOG_W("Observer not found"); } returnresult;}/*** @brief 通知所有观察者*/voidsubject_notify(subject_t*subject,void*data){ rt_base_tlevel; RT_ASSERT(subject != RT_NULL); /* 关闭中断并获取自旋锁 */ level =rt_spin_lock_irqsave(&subject->lock); /* 遍历所有观察者并调用更新函数 */ rt_list_t*node, *temp; rt_list_for_each_safe(node, temp, &subject->observer_list) { observer_t*observer =rt_list_entry(node,observer_t, list); if(observer->update != RT_NULL) { /* 恢复中断后再调用回调函数,避免在中断上下文中执行用户代码 */ rt_spin_unlock_irqrestore(&subject->lock, level); observer->update(observer, subject, data); level =rt_spin_lock_irqsave(&subject->lock); } } /* 恢复中断并释放自旋锁 */ rt_spin_unlock_irqrestore(&subject->lock, level); LOG_D("All observers notified");}/*** @brief 初始化观察者*/rt_err_tobserver_init(observer_t*observer,observer_update_func_tupdate,void*user_data){ RT_ASSERT(observer != RT_NULL); RT_ASSERT(update != RT_NULL); /* 初始化链表节点 */ rt_list_init(&observer->list); /* 设置回调函数和用户数据 */ observer->update = update; observer->user_data = user_data; LOG_D("Observer initialized"); returnRT_EOK;}/*** @brief 获取观察者数量*/intsubject_get_observer_count(subject_t*subject){ rt_base_tlevel; intcount =0; RT_ASSERT(subject != RT_NULL); /* 关闭中断并获取自旋锁 */ level =rt_spin_lock_irqsave(&subject->lock); /* 统计观察者数量 */ rt_list_t*node; rt_list_for_each(node, &subject->observer_list) { count++; } /* 恢复中断并释放自旋锁 */ rt_spin_unlock_irqrestore(&subject->lock, level); returncount;}

observer.h

/***@fileobserver.h*@brief观察者模式头文件*@author*@version1.0*@date2026-02-10** 基于RT-Thread rt_list实现的观察者模式* 支持添加观察者、移除观察者、通知观察者等功能*/#ifndef __OBSERVER_H__#define __OBSERVER_H__#include#include#ifdef __cplusplusextern"C"{#endif/***@brief观察者更新回调函数类型**@paramobserver 观察者指针*@paramsubject 被观察者指针*@paramdata 传递的数据*/typedefvoid(*observer_update_func_t)(void*observer,void*subject,void*data);/***@brief观察者结构体*/typedefstructobserver{ rt_list_tlist; /**< 链表节点,用于串连观察者 */    observer_update_func_t update;      /**< 更新回调函数 */    void *user_data;                    /**< 用户自定义数据 */} observer_t;/** * @brief 被观察者结构体 */typedefstructsubject{    rt_list_t observer_list;            /**< 观察者链表头 */    structrt_spinlocklock;            /**< 自旋锁,保护观察者链表 */} subject_t;/** * @brief 初始化被观察者 *  * @param subject 被观察者对象指针 * @return rt_err_t RT_EOK表示成功,其他表示失败 */rt_err_tsubject_init(subject_t *subject);/** * @brief 添加观察者 *  * @param subject 被观察者对象指针 * @param observer 观察者对象指针 * @return rt_err_t RT_EOK表示成功,其他表示失败 */rt_err_tsubject_add_observer(subject_t *subject, observer_t *observer);/** * @brief 移除观察者 *  * @param subject 被观察者对象指针 * @param observer 观察者对象指针 * @return rt_err_t RT_EOK表示成功,其他表示失败 */rt_err_tsubject_remove_observer(subject_t *subject, observer_t *observer);/** * @brief 通知所有观察者 *  * @param subject 被观察者对象指针 * @param data 传递给观察者的数据 */voidsubject_notify(subject_t *subject, void *data);/** * @brief 初始化观察者 *  * @param observer 观察者对象指针 * @param update 更新回调函数 * @param user_data 用户自定义数据 * @return rt_err_t RT_EOK表示成功,其他表示失败 */rt_err_tobserver_init(observer_t *observer, observer_update_func_t update, void *user_data);/** * @brief 获取观察者数量 *  * @param subject 被观察者对象指针 * @return int 观察者数量 */intsubject_get_observer_count(subject_t *subject);#ifdef __cplusplus}#endif#endif/* __OBSERVER_H__ */

observer_demo.c

/*** @file observer_example.c* @brief 观察者模式使用示例* @author* @version 1.0* @date 2026-02-10** 演示如何使用基于RT-Thread rt_list的观察者模式*/#include"observer.h"#include#defineDBG_TAG "OBS_EX"#defineDBG_LVL DBG_LOG#include/* 定义一个具体的数据结构作为被观察对象 */typedefstruct{ subject_tsubject; /* 继承被观察者 */ inttemperature; /* 温度数据 */ inthumidity; /* 湿度数据 */}sensor_data_t;/* 定义观察者类型 */typedefstruct{ observer_tobserver; /* 继承观察者 */ charname[32]; /* 观察者名称 */}sensor_observer_t;/* 观察者1的更新回调函数 */staticvoidtemperature_observer_update(void*observer,void*subject,void*data){ sensor_observer_t*temp_observer = (sensor_observer_t*)observer; sensor_data_t*sensor_data = (sensor_data_t*)subject; rt_kprintf("Temperature Observer [%s]: Temperature = %d°C, Humidity = %d%%\n", temp_observer->name, sensor_data->temperature, sensor_data->humidity);}/* 观察者2的更新回调函数 */staticvoidhumidity_observer_update(void*observer,void*subject,void*data){ sensor_observer_t*humi_observer = (sensor_observer_t*)observer; sensor_data_t*sensor_data = (sensor_data_t*)subject; rt_kprintf("Humidity Observer [%s]: Temperature = %d°C, Humidity = %d%%\n", humi_observer->name, sensor_data->temperature, sensor_data->humidity);}/* 观察者3的更新回调函数 */staticvoiddisplay_observer_update(void*observer,void*subject,void*data){ sensor_observer_t*display_observer = (sensor_observer_t*)observer; sensor_data_t*sensor_data = (sensor_data_t*)subject; rt_kprintf("Display Observer [%s]: Updating display with T=%d°C, H=%d%%\n", display_observer->name, sensor_data->temperature, sensor_data->humidity);}/* 测试函数 */staticvoidobserver_test(void){ /* 创建被观察者 */ sensor_data_tsensor_data; sensor_data.temperature =25; sensor_data.humidity =60; /* 初始化被观察者 */ if(subject_init(&sensor_data.subject) != RT_EOK) { LOG_E("Failed to initialize subject"); return; } /* 创建观察者 */ sensor_observer_ttemp_observer; sensor_observer_thumi_observer; sensor_observer_tdisplay_observer; /* 初始化观察者 */ rt_strncpy(temp_observer.name,"TempMonitor",sizeof(temp_observer.name)); if(observer_init(&temp_observer.observer, temperature_observer_update, RT_NULL) != RT_EOK) { LOG_E("Failed to initialize temperature observer"); return; } rt_strncpy(humi_observer.name,"HumiMonitor",sizeof(humi_observer.name)); if(observer_init(&humi_observer.observer, humidity_observer_update, RT_NULL) != RT_EOK) { LOG_E("Failed to initialize humidity observer"); return; } rt_strncpy(display_observer.name,"DisplayCtrl",sizeof(display_observer.name)); if(observer_init(&display_observer.observer, display_observer_update, RT_NULL) != RT_EOK) { LOG_E("Failed to initialize display observer"); return; } LOG_I("=== Observer Pattern Test Start ==="); /* 添加观察者 */ LOG_I("Adding observers..."); subject_add_observer(&sensor_data.subject, &temp_observer.observer); subject_add_observer(&sensor_data.subject, &humi_observer.observer); subject_add_observer(&sensor_data.subject, &display_observer.observer); LOG_I("Current observer count: %d",subject_get_observer_count(&sensor_data.subject)); /* 模拟数据变化并通知观察者 */ LOG_I("\n--- Test 1: Initial data notification ---"); subject_notify(&sensor_data.subject, RT_NULL); LOG_I("\n--- Test 2: Temperature change ---"); sensor_data.temperature =28; subject_notify(&sensor_data.subject, RT_NULL); LOG_I("\n--- Test 3: Humidity change ---"); sensor_data.humidity =65; subject_notify(&sensor_data.subject, RT_NULL); LOG_I("\n--- Test 4: Both changes ---"); sensor_data.temperature =30; sensor_data.humidity =70; subject_notify(&sensor_data.subject, RT_NULL); /* 移除一个观察者 */ LOG_I("\n--- Test 5: Remove temperature observer ---"); subject_remove_observer(&sensor_data.subject, &temp_observer.observer); LOG_I("Observer count after removal: %d",subject_get_observer_count(&sensor_data.subject)); LOG_I("\n--- Test 6: Notify remaining observers ---"); sensor_data.temperature =22; sensor_data.humidity =55; subject_notify(&sensor_data.subject, RT_NULL); /* 尝试重复添加同一个观察者 */ LOG_I("\n--- Test 7: Try to add duplicate observer ---"); if(subject_add_observer(&sensor_data.subject, &humi_observer.observer) != RT_EOK) { LOG_W("Duplicate observer rejected as expected"); } /* 尝试移除不存在的观察者 */ LOG_I("\n--- Test 8: Try to remove non-existent observer ---"); sensor_observer_tfake_observer; observer_init(&fake_observer.observer, temperature_observer_update, RT_NULL); if(subject_remove_observer(&sensor_data.subject, &fake_observer.observer) != RT_EOK) { LOG_W("Non-existent observer removal failed as expected"); } LOG_I("\n=== Observer Pattern Test Complete ===");}/* 在FinSH中导出测试命令 */MSH_CMD_EXPORT(observer_test, test observer pattern implementation);

测试

msh />observer_test[12930] V/OBS_EX tshell: === Observer Pattern Test Start ===[12937] V/OBS_EX tshell: Adding observers...[12942] V/OBS_EX tshell: Current observer count:3[12947] V/OBS_EX tshell:--- Test1: Initial data notification ---Temperature Observer [TempMonitor]: Temperature =25°C, Humidity =60%Humidity Observer [HumiMonitor]: Temperature =25°C, Humidity =60%Display Observer [DisplayCtrl]: Updating displaywithT=25°C, H=60%[12972] V/OBS_EX tshell:--- Test2: Temperature change ---Temperature Observer [TempMonitor]: Temperature =28°C, Humidity =60%Humidity Observer [HumiMonitor]: Temperature =28°C, Humidity =60%Display Observer [DisplayCtrl]: Updating displaywithT=28°C, H=60%[12997] V/OBS_EX tshell:--- Test3: Humidity change ---Temperature Observer [TempMonitor]: Temperature =28°C, Humidity =65%Humidity Observer [HumiMonitor]: Temperature =28°C, Humidity =65%Display Observer [DisplayCtrl]: Updating displaywithT=28°C, H=65%[13022] V/OBS_EX tshell:--- Test4: Both changes ---Temperature Observer [TempMonitor]: Temperature =30°C, Humidity =70%Humidity Observer [HumiMonitor]: Temperature =30°C, Humidity =70%Display Observer [DisplayCtrl]: Updating displaywithT=30°C, H=70%[13046] V/OBS_EX tshell:--- Test5: Remove temperature observer ---[13053] V/OBS_EX tshell: Observer count after removal:2[13059] V/OBS_EX tshell:--- Test6: Notify remaining observers ---Humidity Observer [HumiMonitor]: Temperature =22°C, Humidity =55%Display Observer [DisplayCtrl]: Updating displaywithT=22°C, H=55%[13078] V/OBS_EX tshell:--- Test7: Try toaddduplicate observer ---[13086] W/OBSERVER tshell: Observer already exists[13091] W/OBS_EX tshell: Duplicate observer rejectedasexpected[13098] V/OBS_EX tshell:--- Test8: Try toremovenon-existent observer ---[13105] W/OBSERVER tshell: Observernotfound[13110] W/OBS_EX tshell: Non-existent observer removal failedasexpected[13118] V/OBS_EX tshell:=== Observer Pattern Test Complete ===

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式系统
    +关注

    关注

    41

    文章

    3839

    浏览量

    134023
  • 开发者
    +关注

    关注

    1

    文章

    793

    浏览量

    18097
  • RT-Thread
    +关注

    关注

    32

    文章

    1654

    浏览量

    45399
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    RT-Thread Vector软件包:嵌入式开发的动态数组容器 | 技术集结

    RT-Thread Vector软件包:嵌入式开发的动态数组容器 | 技术集结
    的头像 发表于 01-25 09:33 5732次阅读
    <b class='flag-5'>RT-Thread</b> Vector软件包:嵌入式开发的动态数组容器 | <b class='flag-5'>技术</b><b class='flag-5'>集结</b>

    基于观察者模式的屏幕布局控件设计

    观察者模式作为设计模式中行为模式的一种,解决了上述具有一对多依赖关系对象重用问题。文中分析观察者
    发表于 02-13 16:20 4次下载
    基于<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>的屏幕布局控件设计

    Java设计模式分析之观察者

    之间的一对多依赖,当一个对象改变状态时,它的所有依赖都会受到通知并自动更新。 主题接口(Subject):对象使用此接口注册为观察者,或者把自己从观察者删除。 具体主题(Concr
    发表于 09-26 17:36 0次下载

    Java8 环境下实现观察者模式的实例分析

    的一个组成部分。目前虽然已经有大量关于观察者模式的文章,但它们都专注于 Java 实现,却忽视了开发
    发表于 10-12 16:09 0次下载
    <b class='flag-5'>在</b> Java8 环境下<b class='flag-5'>实现</b><b class='flag-5'>观察者</b><b class='flag-5'>模式</b>的实例分析

    RT-Thread全球技术大会:萤石研发团队使用RT-Thread技术挑战

    RT-Thread全球技术大会:研发团队使用RT-Thread技术挑战         审核编辑:彭静
    的头像 发表于 05-27 11:36 2901次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:萤石研发团队使用<b class='flag-5'>RT-Thread</b>的<b class='flag-5'>技术</b>挑战

    2022 RT-Thread全球技术大会:萤石EZIOT SDK对RT-Thread的支持

    2022 RT-Thread全球技术大会:RT-Thread摄像头及IoT设备上的实践经验分享
    的头像 发表于 05-27 11:08 2080次阅读
    2022 <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:萤石EZIOT SDK对<b class='flag-5'>RT-Thread</b>的支持

    RT-Thread全球技术大会:KconfigRT-Thread的工作机制

    RT-Thread全球技术大会:KconfigRT-Thread的工作机制               审核编辑:彭静
    的头像 发表于 05-27 14:49 2575次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:Kconfig<b class='flag-5'>在</b><b class='flag-5'>RT-Thread</b><b class='flag-5'>中</b>的工作机制

    RT-Thread全球技术大会:RT-Thread上编写测试用例

    RT-Thread全球技术大会:RT-Thread上编写测试用例           审核编辑:彭静
    的头像 发表于 05-27 16:28 2372次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:<b class='flag-5'>在</b><b class='flag-5'>RT-Thread</b>上编写测试用例

    RT-Thread全球技术大会:RT-Thread测试用例集合案例

    RT-Thread全球技术大会:RT-Thread测试用例集合案例           审核编辑:彭静
    的头像 发表于 05-27 16:34 3052次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:<b class='flag-5'>RT-Thread</b>测试用例集合案例

    RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍

    RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍             审核编辑:彭静
    的头像 发表于 05-27 16:52 3043次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:<b class='flag-5'>RT-Thread</b>对POSIX的<b class='flag-5'>实现</b>情况介绍

    RT-Thread全球技术大会:RT-Thread底层汇编及arm与riscv上的差异

    开发秦韦忠,RT-Thread全球技术大会上,以RT-Thread底层汇编及arm与ris
    的头像 发表于 05-28 09:56 2259次阅读
    <b class='flag-5'>RT-Thread</b>全球<b class='flag-5'>技术</b>大会:<b class='flag-5'>RT-Thread</b>底层汇编及<b class='flag-5'>在</b>arm与riscv上的差异

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 9次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植

    什么是观察者设计模式?Golang观察者模式介绍

    当涉及到订单处理系统时,观察者设计模式可以用于实现订单状态的变化和通知。
    的头像 发表于 01-08 10:08 1270次阅读

    观察者网:聚焦 RT-Thread睿赛德开发大会发布多个行业应用操作系统 | 媒体视角

    观察者网报道——睿赛德开发大会在2024RT-Thread睿赛德开发大会上,RT-Thread创始人兼CEO熊谱翔隆重发布了程翧车控系统
    的头像 发表于 12-26 19:42 1431次阅读
    <b class='flag-5'>观察者</b>网:聚焦 <b class='flag-5'>RT-Thread</b>睿赛德开发<b class='flag-5'>者</b>大会发布多个行业应用操作系统 | 媒体视角