单例模式指南:全局资源的安全访问

单例模式指南:全局资源的安全访问

📖 你有没有遇到过这些问题?

想象一下这些生活场景:

场景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. 资源节约:避免重复创建昂贵的资源
  2. 数据一致性:全局共享同一份数据
  3. 访问控制:统一管理对共享资源的访问
  4. 协调行为:避免多个实例间的冲突

🌟 单例模式基本实现

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;
}

📚 参考资料

设计模式

  1. Singleton Pattern - 单例模式详解
  2. Thread-Safe Singleton - 线程安全单例
  3. Double-Checked Locking - 双重检查锁定
  4. Lazy Initialization - 延迟初始化

嵌入式应用

  1. C语言编程规范 - Linux内核编码风格
  2. 嵌入式C语言最佳实践 - GitHub开源编码规范
  3. 单例模式讨论 - Stack Overflow
  4. FreeRTOS官方文档 - 实时操作系统

🏷️ 总结

单例模式就像国家元首

  • 全局唯一确保权威性和一致性
  • 统一访问提供标准化的服务接口
  • 资源控制避免重复创建和资源浪费
  • 状态共享维护全局一致的状态信息

核心原则

  1. 全局唯一 > 多个实例
  2. 延迟创建 > 提前创建
  3. 线程安全 > 竞态条件
  4. 资源控制 > 随意访问

记住这个公式

复制代码
优秀的单例模式 = 全局唯一 + 延迟创建 + 线程安全 + 资源控制

通过本文的学习,我们了解了单例模式的原理和最佳实践,掌握了安全管理全局资源的方法。


单例模式是全局资源的守护者,让你的代码像有序的王国一样运行! 🔒

相关推荐
yujkss3 小时前
23种设计模式之【单例模式模式】-核心原理与 Java实践
java·单例模式·设计模式
Chan163 小时前
【 设计模式 | 行为型模式 观察者模式 】
java·spring boot·后端·spring·观察者模式·设计模式·idea
yujkss4 小时前
23种设计模式之【策略模式】-核心原理与 Java 实践
java·设计模式·策略模式
LeenixP5 小时前
STM32H750xx【QSPI】轮询方式读写GD25Q64E
c语言·stm32·嵌入式硬件·cubemx·stm32h7·keilmdk
小莞尔5 小时前
【51单片机】【protues仿真】基于51单片机心形流水灯系统
c语言·stm32·单片机·嵌入式硬件·51单片机
林文韬3275 小时前
C、C++、Java 和 Python:四大编程语言的对比分析
java·c语言·c++·python
new_daimond5 小时前
设计模式-建造者模式详解
java·设计模式·建造者模式
ytadpole6 小时前
揭秘设计模式:状态设计模式 优雅地管理对象状态
java·后端·设计模式
new_daimond7 小时前
设计模式-中介者模式详解
设计模式·中介者模式