单例模式的核心是确保一个类(或模块)只有一个实例,并提供全局访问点 。在C语言中,没有类的概念,但可以通过静态变量、函数封装实现类似效果,保证某块内存(如配置对象、管理器)仅被初始化一次。
C语言实现单例模式的关键思路
- 隐藏实例:用静态局部变量存储唯一实例(仅在首次访问时初始化)。
- 全局访问点:提供一个接口函数,负责初始化实例并返回其指针(确保仅初始化一次)。
- 线程安全(可选):在多线程环境下,需通过互斥锁避免并发初始化导致的多实例问题。
示例:配置管理器单例(单线程版)
假设需要一个全局唯一的配置管理器,负责加载和提供配置项(如数据库地址、端口)。
步骤1:定义单例结构体(ConfigManager)
c
// 配置管理器结构体(单例的具体数据)
typedef struct {
char db_host[64]; // 数据库地址
int db_port; // 数据库端口
char log_path[128]; // 日志路径
} ConfigManager;
步骤2:实现单例的创建与访问接口
通过静态局部变量存储实例,并用接口函数控制初始化逻辑(仅首次调用时初始化)。
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 静态局部变量:存储唯一实例(文件内可见,外部无法直接访问)
static ConfigManager* instance = NULL;
// 单例的全局访问接口
ConfigManager* ConfigManager_getInstance() {
// 首次调用时初始化实例
if (instance == NULL) {
instance = (ConfigManager*)malloc(sizeof(ConfigManager));
if (instance == NULL) {
printf("内存分配失败\n");
return NULL;
}
// 初始化配置(实际场景可能从文件读取)
strcpy(instance->db_host, "localhost");
instance->db_port = 3306;
strcpy(instance->log_path, "/var/log/app.log");
printf("配置管理器初始化完成\n");
}
return instance;
}
// 单例的销毁接口(可选,程序退出前调用)
void ConfigManager_destroy() {
if (instance != NULL) {
free(instance);
instance = NULL;
printf("配置管理器已销毁\n");
}
}
步骤3:使用单例
无论调用多少次ConfigManager_getInstance
,返回的都是同一个实例。
c
int main() {
// 第一次获取实例:触发初始化
ConfigManager* config1 = ConfigManager_getInstance();
printf("数据库地址: %s, 端口: %d\n", config1->db_host, config1->db_port);
// 第二次获取实例:直接返回已存在的实例
ConfigManager* config2 = ConfigManager_getInstance();
printf("日志路径: %s\n", config2->log_path);
// 验证是否为同一个实例(地址相同)
if (config1 == config2) {
printf("config1 和 config2 是同一个实例\n");
}
// 销毁单例(程序退出前)
ConfigManager_destroy();
return 0;
}
输出结果
配置管理器初始化完成
数据库地址: localhost, 端口: 3306
日志路径: /var/log/app.log
config1 和 config2 是同一个实例
配置管理器已销毁
多线程安全的单例(加锁版)
在多线程环境中,多个线程可能同时进入instance == NULL
的判断,导致创建多个实例。需通过互斥锁保证初始化的原子性。
c
#include <pthread.h> // 需链接pthread库(编译时加 -lpthread)
// 静态实例
static ConfigManager* instance = NULL;
// 互斥锁:保证初始化过程唯一
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 线程安全的单例访问接口
ConfigManager* ConfigManager_getInstance() {
// 双重检查锁(Double-Checked Locking):减少锁开销
if (instance == NULL) { // 第一次检查:避免每次加锁
pthread_mutex_lock(&mutex); // 加锁
if (instance == NULL) { // 第二次检查:确保仅初始化一次
instance = (ConfigManager*)malloc(sizeof(ConfigManager));
// ... 初始化配置 ...
printf("线程安全的配置管理器初始化完成\n");
}
pthread_mutex_unlock(&mutex); // 解锁
}
return instance;
}
核心特点总结
- 唯一性 :通过静态变量
instance
确保全局只有一个实例。 - 延迟初始化:实例在首次访问时才创建,避免程序启动时的不必要开销。
- 封装性 :实例通过
ConfigManager_getInstance
接口访问,外部无法直接修改或创建,保证可控性。 - 线程安全(可选):多线程环境下需加锁,双重检查锁可平衡安全性和性能。
C语言的单例模式常用于全局资源管理(如配置、日志、设备驱动),核心是通过静态变量和函数封装控制实例的创建与访问。