单例模式指南:全局资源的安全访问
📖 你有没有遇到过这些问题?
想象一下这些生活场景:
场景1:公司CEO
方式A:每个部门都有自己的CEO,各自发号施令
方式B:整个公司只有一个CEO,统一管理和决策
哪种方式更有效?
场景2:打印机管理方式A:每个人都认为自己在独占打印机,同时发送任务
方式B:有一个打印机管理员,统一调度所有打印任务
哪种方式更有序?
在编程中,单例模式就像公司CEO和打印机管理员一样重要!
多个实例像多个CEO一样造成混乱:
c
// ❌ 多个配置管理实例,数据不一致
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一样保证一致性:
c
// ✅ 单例模式,全局唯一的配置管理
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. 简单单例模式
基础单例实现
c
// 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);
简单单例实现
c
// 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. 线程安全单例
多线程环境下的安全实现
c
// 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);
线程安全实现
c
// 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. 延迟初始化单例
按需创建的单例
c
// 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. 系统状态管理单例
全局系统状态
c
// 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. 硬件资源管理单例
硬件资源统一管理
c
// 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官方文档 - 实时操作系统
🏷️ 总结
单例模式就像国家元首:
- 全局唯一确保权威性和一致性
- 统一访问提供标准化的服务接口
- 资源控制避免重复创建和资源浪费
- 状态共享维护全局一致的状态信息
核心原则:
- 全局唯一 > 多个实例
- 延迟创建 > 提前创建
- 线程安全 > 竞态条件
- 资源控制 > 随意访问
记住这个公式:
优秀的单例模式 = 全局唯一 + 延迟创建 + 线程安全 + 资源控制
通过本文的学习,我们了解了单例模式的原理和最佳实践,掌握了安全管理全局资源的方法。
单例模式是全局资源的守护者,让你的代码像有序的王国一样运行! 🔒