单例模式指南:全局资源的安全访问
单例模式指南:全局资源的安全访问
📖 你有没有遇到过这些问题?
想象一下这些生活场景:
场景1:公司CEO
方式A:每个部门都有自己的CEO,各自发号施令
方式B:整个公司只有一个CEO,统一管理和决策哪种方式更有效?
场景2:打印机管理
方式A:每个人都认为自己在独占打印机,同时发送任务
方式B:有一个打印机管理员,统一调度所有打印任务哪种方式更有序?
在编程中,单例模式就像公司CEO和打印机管理员一样重要!
多个实例像多个CEO一样造成混乱:
// ❌ 多个配置管理实例,数据不一致
SystemConfig_t config1;
SystemConfig_t config2;
SystemConfig_t config3;void module_a_init(void)
{config1.sensor_type = SENSOR_DS18B20;config1.baudrate = 115200;
}void module_b_init(void)
{config2.sensor_type = SENSOR_LM35; // 与module_a不一致!config2.baudrate = 9600; // 与module_a不一致!
}void module_c_init(void)
{// 使用哪个配置?config1还是config2?if (config3.sensor_type == SENSOR_DS18B20) {// 可能使用了未初始化的config3}
}
单例模式像统一的CEO一样保证一致性:
// ✅ 单例模式,全局唯一的配置管理
SystemConfig_t* SystemConfig_GetInstance(void)
{static SystemConfig_t instance;static bool initialized = false;if (!initialized){// 初始化配置instance.sensor_type = SENSOR_DS18B20;instance.baudrate = 115200;instance.display_type = DISPLAY_LCD1602;initialized = true;}return &instance;
}void module_a_init(void)
{SystemConfig_t *config = SystemConfig_GetInstance();// 所有模块使用同一个配置实例
}void module_b_init(void)
{SystemConfig_t *config = SystemConfig_GetInstance();// 保证配置一致性
}
本文将详细介绍单例模式的原理和最佳实践,帮助开发者安全管理全局资源。
🎯 为什么需要单例模式?
生活中的例子
场景1:国家总统
多实例:每个州都有自己的总统,政令不统一
单例模式:一个国家只有一个总统,政令统一
场景2:数据库连接
多实例:每个模块都创建数据库连接,资源浪费
单例模式:全局共享一个连接池,资源高效利用
单例模式的价值
- 资源节约:避免重复创建昂贵的资源
- 数据一致性:全局共享同一份数据
- 访问控制:统一管理对共享资源的访问
- 协调行为:避免多个实例间的冲突
🌟 单例模式基本实现
1. 简单单例模式
基础单例实现
// simple_singleton.h - 简单单例typedef struct {float temperature_threshold;float pressure_threshold;uint32_t sample_interval_ms;bool alarm_enabled;char device_name[32];
} SystemConfig_t;// 单例接口
SystemConfig_t* SystemConfig_GetInstance(void);
bool SystemConfig_LoadFromEEPROM(void);
bool SystemConfig_SaveToEEPROM(void);
void SystemConfig_SetDefaults(void);
简单单例实现
// system_config.c - 系统配置单例static SystemConfig_t g_system_config;
static bool g_config_initialized = false;/*** @brief 获取系统配置单例* @return 系统配置实例指针*/
SystemConfig_t* SystemConfig_GetInstance(void)
{if (!g_config_initialized){// 懒加载:第一次访问时初始化SystemConfig_SetDefaults();SystemConfig_LoadFromEEPROM();g_config_initialized = true;printf("系统配置单例初始化完成\n");}return &g_system_config;
}/*** @brief 设置默认配置*/
void SystemConfig_SetDefaults(void)
{g_system_config.temperature_threshold = 85.0f;g_system_config.pressure_threshold = 1000.0f;g_system_config.sample_interval_ms = 1000;g_system_config.alarm_enabled = true;strcpy(g_system_config.device_name, "LFS-FlowMeter");printf("系统配置设置为默认值\n");
}/*** @brief 从EEPROM加载配置* @return 加载结果*/
bool SystemConfig_LoadFromEEPROM(void)
{// 检查EEPROM中是否有有效配置uint32_t magic_number;if (!EEPROM_Read(CONFIG_MAGIC_ADDR, &magic_number, sizeof(magic_number))){printf("EEPROM读取失败,使用默认配置\n");return false;}if (magic_number != CONFIG_MAGIC_VALUE){printf("EEPROM中无有效配置,使用默认配置\n");return false;}// 读取配置数据if (EEPROM_Read(CONFIG_DATA_ADDR, &g_system_config, sizeof(g_system_config))){printf("从EEPROM加载配置成功\n");return true;}printf("EEPROM配置数据损坏,使用默认配置\n");return false;
}/*** @brief 保存配置到EEPROM* @return 保存结果*/
bool SystemConfig_SaveToEEPROM(void)
{// 写入魔数uint32_t magic_number = CONFIG_MAGIC_VALUE;if (!EEPROM_Write(CONFIG_MAGIC_ADDR, &magic_number, sizeof(magic_number))){printf("EEPROM魔数写入失败\n");return false;}// 写入配置数据if (EEPROM_Write(CONFIG_DATA_ADDR, &g_system_config, sizeof(g_system_config))){printf("配置保存到EEPROM成功\n");return true;}printf("配置保存到EEPROM失败\n");return false;
}
2. 线程安全单例
多线程环境下的安全实现
// thread_safe_singleton.h - 线程安全单例typedef struct {uint32_t total_pulses;float total_volume;float instantaneous_flow;uint32_t last_update_time;bool is_measuring;
} FlowData_t;// 线程安全的流量数据单例
FlowData_t* FlowData_GetInstance(void);
void FlowData_UpdatePulse(void);
void FlowData_UpdateFlow(float flow);
void FlowData_Reset(void);
bool FlowData_StartMeasurement(void);
void FlowData_StopMeasurement(void);
线程安全实现
// flow_data_singleton.c - 流量数据单例#include "mutex.h"static FlowData_t g_flow_data;
static bool g_flow_data_initialized = false;
static Mutex_t g_flow_data_mutex;/*** @brief 获取流量数据单例(线程安全)* @return 流量数据实例指针*/
FlowData_t* FlowData_GetInstance(void)
{// 双重检查锁定模式if (!g_flow_data_initialized){Mutex_Lock(&g_flow_data_mutex);// 再次检查,防止多线程重复初始化if (!g_flow_data_initialized){memset(&g_flow_data, 0, sizeof(g_flow_data));g_flow_data.last_update_time = GetSystemTick();g_flow_data_initialized = true;printf("流量数据单例初始化完成\n");}Mutex_Unlock(&g_flow_data_mutex);}return &g_flow_data;
}/*** @brief 更新脉冲计数(线程安全)*/
void FlowData_UpdatePulse(void)
{Mutex_Lock(&g_flow_data_mutex);FlowData_t *data = FlowData_GetInstance();data->total_pulses++;data->last_update_time = GetSystemTick();Mutex_Unlock(&g_flow_data_mutex);
}/*** @brief 更新瞬时流量(线程安全)* @param flow 瞬时流量值*/
void FlowData_UpdateFlow(float flow)
{Mutex_Lock(&g_flow_data_mutex);FlowData_t *data = FlowData_GetInstance();data->instantaneous_flow = flow;// 更新累计流量(简化计算)uint32_t current_time = GetSystemTick();uint32_t time_diff = current_time - data->last_update_time;if (time_diff > 0){data->total_volume += flow * (time_diff / 60000.0f); // 转换为分钟}data->last_update_time = current_time;Mutex_Unlock(&g_flow_data_mutex);
}/*** @brief 重置流量数据(线程安全)*/
void FlowData_Reset(void)
{Mutex_Lock(&g_flow_data_mutex);FlowData_t *data = FlowData_GetInstance();data->total_pulses = 0;data->total_volume = 0.0f;data->instantaneous_flow = 0.0f;data->last_update_time = GetSystemTick();printf("流量数据已重置\n");Mutex_Unlock(&g_flow_data_mutex);
}
3. 延迟初始化单例
按需创建的单例
// lazy_singleton.c - 延迟初始化单例typedef struct {char log_buffer[LOG_BUFFER_SIZE];size_t buffer_index;bool buffer_full;uint32_t log_count;FILE *log_file;
} Logger_t;static Logger_t *g_logger_instance = NULL;
static bool g_logger_cleanup_registered = false;/*** @brief 清理日志单例*/
static void logger_cleanup(void)
{if (g_logger_instance){if (g_logger_instance->log_file){fclose(g_logger_instance->log_file);}free(g_logger_instance);g_logger_instance = NULL;printf("日志单例已清理\n");}
}/*** @brief 获取日志单例(延迟初始化)* @return 日志实例指针*/
Logger_t* Logger_GetInstance(void)
{if (g_logger_instance == NULL){g_logger_instance = malloc(sizeof(Logger_t));if (g_logger_instance == NULL){printf("日志单例创建失败:内存不足\n");return NULL;}// 初始化日志实例memset(g_logger_instance->log_buffer, 0, LOG_BUFFER_SIZE);g_logger_instance->buffer_index = 0;g_logger_instance->buffer_full = false;g_logger_instance->log_count = 0;g_logger_instance->log_file = fopen("system.log", "a");// 注册清理函数if (!g_logger_cleanup_registered){atexit(logger_cleanup);g_logger_cleanup_registered = true;}printf("日志单例创建成功\n");}return g_logger_instance;
}/*** @brief 写入日志* @param level 日志级别* @param message 日志消息*/
void Logger_Write(LogLevel_t level, const char *message)
{Logger_t *logger = Logger_GetInstance();if (logger == NULL){return;}// 格式化日志消息char formatted_msg[256];uint32_t timestamp = GetSystemTick();snprintf(formatted_msg, sizeof(formatted_msg), "[%lu] [%s] %s\n", timestamp, LogLevel_ToString(level), message);// 写入缓冲区size_t msg_len = strlen(formatted_msg);if (logger->buffer_index + msg_len < LOG_BUFFER_SIZE){strcpy(&logger->log_buffer[logger->buffer_index], formatted_msg);logger->buffer_index += msg_len;}else{logger->buffer_full = true;}// 写入文件if (logger->log_file){fprintf(logger->log_file, "%s", formatted_msg);fflush(logger->log_file);}logger->log_count++;
}
🎨 LFS项目中的单例应用
1. 系统状态管理单例
全局系统状态
// system_state_singleton.c - 系统状态单例typedef enum {SYSTEM_STATE_INITIALIZING = 0,SYSTEM_STATE_IDLE,SYSTEM_STATE_MEASURING,SYSTEM_STATE_CALIBRATING,SYSTEM_STATE_ERROR,SYSTEM_STATE_SHUTDOWN
} SystemState_t;typedef struct {SystemState_t current_state;SystemState_t previous_state;uint32_t state_enter_time;uint32_t total_runtime;uint32_t error_count;char last_error_message[64];bool maintenance_mode;
} SystemStatus_t;static SystemStatus_t g_system_status;
static bool g_status_initialized = false;/*** @brief 获取系统状态单例*/
SystemStatus_t* SystemStatus_GetInstance(void)
{if (!g_status_initialized){g_system_status.current_state = SYSTEM_STATE_INITIALIZING;g_system_status.previous_state = SYSTEM_STATE_INITIALIZING;g_system_status.state_enter_time = GetSystemTick();g_system_status.total_runtime = 0;g_system_status.error_count = 0;g_system_status.maintenance_mode = false;memset(g_system_status.last_error_message, 0, sizeof(g_system_status.last_error_message));g_status_initialized = true;printf("系统状态单例初始化完成\n");}return &g_system_status;
}/*** @brief 设置系统状态*/
void SystemStatus_SetState(SystemState_t new_state)
{SystemStatus_t *status = SystemStatus_GetInstance();if (status->current_state != new_state){status->previous_state = status->current_state;status->current_state = new_state;status->state_enter_time = GetSystemTick();printf("系统状态变更: %d -> %d\n", status->previous_state, new_state);// 记录状态变更日志Logger_Write(LOG_LEVEL_INFO, "系统状态变更");}
}/*** @brief 记录系统错误*/
void SystemStatus_RecordError(const char *error_message)
{SystemStatus_t *status = SystemStatus_GetInstance();status->error_count++;strncpy(status->last_error_message, error_message, sizeof(status->last_error_message) - 1);printf("系统错误 #%lu: %s\n", status->error_count, error_message);// 记录错误日志Logger_Write(LOG_LEVEL_ERROR, error_message);
}
2. 硬件资源管理单例
硬件资源统一管理
// hardware_manager_singleton.c - 硬件管理单例typedef struct {bool gpio_initialized;bool uart_initialized;bool adc_initialized;bool timer_initialized;uint32_t gpio_usage_mask;uint8_t uart_baudrate_index;uint8_t adc_channels_used;uint32_t timer_prescaler;// 资源锁定状态bool gpio_locked[16];bool uart_locked;bool adc_locked;bool timer_locked;
} HardwareManager_t;static HardwareManager_t g_hw_manager;
static bool g_hw_manager_initialized = false;/*** @brief 获取硬件管理器单例*/
HardwareManager_t* HardwareManager_GetInstance(void)
{if (!g_hw_manager_initialized){memset(&g_hw_manager, 0, sizeof(g_hw_manager));g_hw_manager_initialized = true;printf("硬件管理器单例初始化完成\n");}return &g_hw_manager;
}/*** @brief 申请GPIO资源*/
bool HardwareManager_RequestGPIO(uint8_t pin)
{HardwareManager_t *hw = HardwareManager_GetInstance();if (pin >= 16 || hw->gpio_locked[pin]){printf("GPIO %d 申请失败:已被占用\n", pin);return false;}hw->gpio_locked[pin] = true;hw->gpio_usage_mask |= (1 << pin);printf("GPIO %d 申请成功\n", pin);return true;
}/*** @brief 释放GPIO资源*/
void HardwareManager_ReleaseGPIO(uint8_t pin)
{HardwareManager_t *hw = HardwareManager_GetInstance();if (pin < 16 && hw->gpio_locked[pin]){hw->gpio_locked[pin] = false;hw->gpio_usage_mask &= ~(1 << pin);printf("GPIO %d 已释放\n", pin);}
}/*** @brief 申请UART资源*/
bool HardwareManager_RequestUART(uint32_t baudrate)
{HardwareManager_t *hw = HardwareManager_GetInstance();if (hw->uart_locked){printf("UART申请失败:已被占用\n");return false;}hw->uart_locked = true;hw->uart_baudrate_index = GetBaudrateIndex(baudrate);printf("UART申请成功,波特率: %lu\n", baudrate);return true;
}
📚 参考资料
设计模式
- Singleton Pattern - 单例模式详解
- Thread-Safe Singleton - 线程安全单例
- Double-Checked Locking - 双重检查锁定
- Lazy Initialization - 延迟初始化
嵌入式应用
- C语言编程规范 - Linux内核编码风格
- 嵌入式C语言最佳实践 - GitHub开源编码规范
- 单例模式讨论 - Stack Overflow
- FreeRTOS官方文档 - 实时操作系统
🏷️ 总结
单例模式就像国家元首:
- 全局唯一确保权威性和一致性
- 统一访问提供标准化的服务接口
- 资源控制避免重复创建和资源浪费
- 状态共享维护全局一致的状态信息
核心原则:
- 全局唯一 > 多个实例
- 延迟创建 > 提前创建
- 线程安全 > 竞态条件
- 资源控制 > 随意访问
记住这个公式:
优秀的单例模式 = 全局唯一 + 延迟创建 + 线程安全 + 资源控制
通过本文的学习,我们了解了单例模式的原理和最佳实践,掌握了安全管理全局资源的方法。
单例模式是全局资源的守护者,让你的代码像有序的王国一样运行! 🔒