文章目录
-
-
-
- 一、单例模式的定义与核心价值
- [二、C 语言实现单例模式的核心思路](#二、C 语言实现单例模式的核心思路)
- [三、 5 个实例](#三、 5 个实例)
-
- [实例 1:基础懒汉式单例(日志管理器)](#实例 1:基础懒汉式单例(日志管理器))
- [实例 2:饿汉式单例(配置管理器)](#实例 2:饿汉式单例(配置管理器))
- [实例 3:内核风格单例(硬件控制器)](#实例 3:内核风格单例(硬件控制器))
- [实例 4:单例模式 + 接口封装(缓存管理器)](#实例 4:单例模式 + 接口封装(缓存管理器))
- [实例 5:线程安全的懒汉式单例(计数器服务)](#实例 5:线程安全的懒汉式单例(计数器服务))
- [四、Linux 内核中的单例模式应用](#四、Linux 内核中的单例模式应用)
- 五、实现注意事项
- 六、补充说明
-
-
一、单例模式的定义与核心价值
单例模式(Singleton Pattern)是一种创建型设计模式,其核心是确保一个类(或结构体)在系统中仅有一个实例,并提供一个全局访问点。该模式通过限制对象的创建次数,避免重复分配资源,同时保证全局状态的一致性。
存在的意义:当系统中某些资源(如配置管理器、日志器、硬件控制器)需要被全局共享且仅需一份实例时(如多个模块同时操作同一硬件,重复初始化会导致冲突),直接创建多个实例会造成资源浪费、状态不一致或逻辑错误。单例模式通过强制单一实例,确保资源的唯一性和全局可访问性。
解决的问题:
- 全局共享资源被多次创建,导致内存 / 硬件资源浪费;
- 多实例操作同一资源(如配置文件、硬件寄存器),引发状态冲突(如配置被重复覆盖);
- 缺乏统一访问点,不同模块获取的实例不一致,导致逻辑混乱。
二、C 语言实现单例模式的核心思路
UML图例
- 单例类 Singleton 私有构造函数(防止外部实例化);静态私有实例变量(存储唯一实例);静态公有方法(如getInstance()),提供全局访问点,确保返回唯一实例。
- 客户端 Client 通过单例类的静态方法获取实例,调用实例的业务方法;不关心实例的创建细节,仅需保证使用的是同一个实例。

C 语言通过静态全局变量存储唯一实例 +封装创建函数控制实例生成实现单例,核心要点:
- 隐藏构造函数 :通过
static修饰结构体定义或限制初始化函数的可见性,防止外部直接创建实例; - 控制实例创建 :提供一个全局访问函数(如
get_instance()),确保仅在首次调用时创建实例,后续调用直接返回已存在的实例; - 线程安全保障:多线程环境下,通过互斥锁或原子操作防止并发创建导致的多实例问题;
- 资源释放 :提供销毁函数(如
destroy_instance()),在程序退出时释放单例占用的资源(可选,内核场景中可能随系统退出自动释放)。
三、 5 个实例
实例 1:基础懒汉式单例(日志管理器)
"懒汉式" 指实例在首次使用时才创建,适合资源占用较大、可能不被使用的场景(如日志管理器)。
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// 1. 单例结构体:日志管理器(全局唯一)
typedef struct {
FILE* log_file; // 日志文件句柄
const char* log_level;// 日志级别(INFO/ERROR)
} LogManager;
// 2. 静态全局变量存储唯一实例(隐藏在.c文件中,外部不可直接访问)
static LogManager* instance = NULL;
// 互斥锁:保证多线程下实例创建的原子性
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
// 3. 全局访问函数:获取单例实例(懒汉式创建)
LogManager* log_manager_get_instance(const char* filename, const char* level) {
// 双重检查锁定(Double-Checked Locking):减少锁开销
if (instance == NULL) {
pthread_mutex_lock(&log_mutex); // 加锁
if (instance == NULL) { // 再次检查(防止并发场景下重复创建)
instance = malloc(sizeof(LogManager));
if (instance) {
// 初始化单例:打开日志文件、设置级别
instance->log_file = fopen(filename, "a");
instance->log_level = level; // 简化处理:假设level为常量字符串
printf("LogManager instance created. File: %s, Level: %s\n", filename, level);
}
}
pthread_mutex_unlock(&log_mutex); // 解锁
}
return instance;
}
// 4. 日志写入接口(单例的核心功能)
void log_manager_write(LogManager* self, const char* msg) {
if (!self || !self->log_file) return;
fprintf(self->log_file, "[%s] %s\n", self->log_level, msg);
fflush(self->log_file); // 立即刷新到文件
}
// 5. 单例销毁函数
void log_manager_destroy() {
pthread_mutex_lock(&log_mutex);
if (instance) {
if (instance->log_file) {
fclose(instance->log_file);
printf("LogManager file closed\n");
}
free(instance);
instance = NULL;
printf("LogManager instance destroyed\n");
}
pthread_mutex_unlock(&log_mutex);
pthread_mutex_destroy(&log_mutex); // 销毁互斥锁
}
// 模拟多线程场景:多个线程获取单例并写入日志
void* thread_func(void* arg) {
const char* thread_name = (const char*)arg;
LogManager* logger = log_manager_get_instance("app.log", "INFO");
// 每个线程写入一条日志
char msg[128];
snprintf(msg, sizeof(msg), "Message from %s", thread_name);
log_manager_write(logger, msg);
return NULL;
}
int main() {
// 1. 主线程获取单例并写入日志
LogManager* main_logger = log_manager_get_instance("app.log", "INFO");
log_manager_write(main_logger, "Main thread started");
// 2. 启动3个线程,验证单例唯一性
pthread_t t1, t2, t3;
pthread_create(&t1, NULL, thread_func, "Thread 1");
pthread_create(&t2, NULL, thread_func, "Thread 2");
pthread_create(&t3, NULL, thread_func, "Thread 3");
// 等待线程结束
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
// 3. 销毁单例
log_manager_destroy();
return 0;
}
上面的代码,运行结果如下
LogManager instance created. File: app.log, Level: INFO
LogManager file closed
LogManager instance destroyed
其UML图如下,

即Client在首次调用log_manager_get_instance时创建实例,通过双重检查锁定(DCL)保证多线程安全,同时减少锁的开销;
,static保证全局仅存在一个LogManager实例,所有线程共享同一日志文件,避免多实例导致的文件操作冲突。
实例 2:饿汉式单例(配置管理器)
"饿汉式" 指实例在程序启动时(或模块加载时)就创建,适合资源占用小、必然会被使用的场景(如配置管理器)。
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 1. 单例结构体:配置管理器(存储全局配置)
#define MAX_CONFIG_KEYS 10
typedef struct {
char keys[MAX_CONFIG_KEYS][32]; // 配置键
char values[MAX_CONFIG_KEYS][64]; // 配置值
int count; // 已加载的配置数量
} ConfigManager;
// 2. 饿汉式:程序启动时直接初始化单例(静态变量在main前初始化)
static ConfigManager instance = {
.count = 0 // 初始化为空配置
};
// 3. 全局访问函数:直接返回预初始化的实例
ConfigManager* config_manager_get_instance() {
return &instance; // 无需创建,直接返回静态实例
}
// 4. 配置管理功能:添加配置
void config_manager_add(ConfigManager* self, const char* key, const char* value) {
if (!self || self->count >= MAX_CONFIG_KEYS) return;
strncpy(self->keys[self->count], key, sizeof(self->keys[0])-1);
strncpy(self->values[self->count], value, sizeof(self->values[0])-1);
self->count++;
printf("Added config: %s=%s\n", key, value);
}
// 5. 配置管理功能:查询配置
const char* config_manager_get(ConfigManager* self, const char* key) {
if (!self) return NULL;
for (int i = 0; i < self->count; i++) {
if (strcmp(self->keys[i], key) == 0) {
return self->values[i];
}
}
return "NOT_FOUND";
}
// 模拟不同模块使用配置管理器
void module_a_init() {
ConfigManager* config = config_manager_get_instance();
config_manager_add(config, "module_a_port", "8080");
printf("Module A: port=%s\n", config_manager_get(config, "module_a_port"));
}
void module_b_init() {
ConfigManager* config = config_manager_get_instance();
config_manager_add(config, "module_b_timeout", "30");
printf("Module B: timeout=%s\n", config_manager_get(config, "module_b_timeout"));
}
int main() {
// 饿汉式单例在main前已初始化
printf("Main: Initial config count=%d\n", config_manager_get_instance()->count);
// 不同模块共享同一配置实例
module_a_init();
module_b_init();
// 验证配置全局可见
ConfigManager* main_config = config_manager_get_instance();
printf("Main: module_a_port=%s\n", config_manager_get(main_config, "module_a_port"));
printf("Main: module_b_timeout=%s\n", config_manager_get(main_config, "module_b_timeout"));
return 0; // 静态实例随程序退出自动释放,无需手动销毁
}
上面的代码,运行结果如下
Main: Initial config count=0
Added config: module_a_port=8080
Module A: port=8080
Added config: module_b_timeout=30
Module B: timeout=30
Main: module_a_port=8080
Main: module_b_timeout=30
其的UML图如下

即通过静态全局变量instance在程序启动时(main函数前)完成初始化,无需考虑线程安全(初始化在单线程阶段
实例 3:内核风格单例(硬件控制器)
模拟 Linux 内核中硬件控制器的单例设计(如 DMA 控制器、中断控制器),硬件资源唯一,需通过单例确保独占访问。
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <pthread.h>
#include <stdbool.h>
// 1. 单例结构体:DMA控制器(硬件资源唯一)
typedef struct {
uint32_t base_addr; // 硬件基地址
bool in_use; // 是否正在使用
pthread_mutex_t lock; // 硬件访问锁
} DMAController;
// 2. 静态全局实例(内核中通常放在.bss段)
static DMAController* dma_instance = NULL;
static pthread_mutex_t dma_init_lock = PTHREAD_MUTEX_INITIALIZER;
// 3. 内核风格初始化函数:获取单例(模拟内核模块初始化)
int dma_controller_init(uint32_t base_addr) {
pthread_mutex_lock(&dma_init_lock);
if (dma_instance) {
// 已初始化,直接返回成功
pthread_mutex_unlock(&dma_init_lock);
return 0;
}
// 首次初始化:分配并初始化实例
dma_instance = malloc(sizeof(DMAController));
if (!dma_instance) {
pthread_mutex_unlock(&dma_init_lock);
return -1; // 内存分配失败
}
// 初始化硬件控制器
dma_instance->base_addr = base_addr;
dma_instance->in_use = false;
pthread_mutex_init(&dma_instance->lock, NULL);
printf("DMA Controller initialized at 0x%08X\n", base_addr);
pthread_mutex_unlock(&dma_init_lock);
return 0;
}
// 4. 全局访问函数:获取控制器实例(内核中通常通过API访问)
DMAController* dma_controller_get() {
return dma_instance; // 假设已初始化(内核中通常要求先init后使用)
}
// 5. 硬件操作接口:申请DMA通道(独占访问)
int dma_request_channel(DMAController* self) {
if (!self) return -1;
pthread_mutex_lock(&self->lock);
if (self->in_use) {
pthread_mutex_unlock(&self->lock);
return -1; // 已被占用
}
self->in_use = true;
printf("DMA channel requested (base: 0x%08X)\n", self->base_addr);
pthread_mutex_unlock(&self->lock);
return 0;
}
// 6. 硬件操作接口:释放DMA通道
void dma_release_channel(DMAController* self) {
if (!self) return;
pthread_mutex_lock(&self->lock);
self->in_use = false;
printf("DMA channel released (base: 0x%08X)\n", self->base_addr);
pthread_mutex_unlock(&self->lock);
}
// 模拟内核模块使用DMA控制器
void kernel_module_a() {
DMAController* dma = dma_controller_get();
if (dma_request_channel(dma) == 0) {
// 模拟使用DMA传输数据
printf("Module A: DMA transfer completed\n");
dma_release_channel(dma);
} else {
printf("Module A: Failed to get DMA channel\n");
}
}
void kernel_module_b() {
DMAController* dma = dma_controller_get();
if (dma_request_channel(dma) == 0) {
// 模拟使用DMA传输数据
printf("Module B: DMA transfer completed\n");
dma_release_channel(dma);
} else {
printf("Module B: Failed to get DMA channel\n");
}
}
int main() {
// 初始化DMA控制器(内核中通常在驱动加载时调用)
if (dma_controller_init(0xFFFF0000) != 0) {
printf("Failed to initialize DMA controller\n");
return -1;
}
// 两个模块共享同一DMA控制器
kernel_module_a();
kernel_module_b(); // 此时通道已释放,可正常申请
// 内核中通常不手动销毁(随系统 shutdown 释放)
return 0;
}
以上代码运行结果如下
DMA Controller initialized at 0xFFFF0000
DMA channel requested (base: 0xFFFF0000)
Module A: DMA transfer completed
DMA channel released (base: 0xFFFF0000)
DMA channel requested (base: 0xFFFF0000)
Module B: DMA transfer completed
DMA channel released (base: 0xFFFF0000)
其UML图例如下:

即模拟内核中硬件控制器的单例设计,硬件资源(如 DMA 控制器)物理唯一,通过单例确保只有一个实例管理硬件;同时提供dma_request_channel和dma_release_channel接口控制硬件独占访问,避免多模块同时操作硬件导致冲突。
实例 4:单例模式 + 接口封装(缓存管理器)
将单例的实现细节隐藏在接口后,外部仅通过函数访问,不直接操作结构体,符合 C 语言 "信息隐藏" 的设计思想。
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// 1. 前向声明:隐藏结构体细节(仅在.c文件中定义)
typedef struct CacheManager CacheManager;
// 2. 对外接口声明(.h文件中可见)
CacheManager* cache_manager_get_instance();
void cache_manager_set(CacheManager* self, const char* key, const char* value);
const char* cache_manager_get(CacheManager* self, const char* key);
void cache_manager_destroy();
// 3. 结构体定义(仅在.c文件中可见,外部无法直接访问成员)
struct CacheManager {
char keys[5][32]; // 缓存键
char values[5][64]; // 缓存值
int count; // 缓存数量
pthread_mutex_t lock; // 线程安全锁
};
// 4. 静态全局实例(隐藏实现)
static CacheManager* instance = NULL;
static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
// 5. 接口实现:获取实例
CacheManager* cache_manager_get_instance() {
if (!instance) {
pthread_mutex_lock(&init_lock);
if (!instance) {
instance = malloc(sizeof(CacheManager));
if (instance) {
instance->count = 0;
pthread_mutex_init(&instance->lock, NULL);
printf("CacheManager instance created\n");
}
}
pthread_mutex_unlock(&init_lock);
}
return instance;
}
// 6. 接口实现:设置缓存
void cache_manager_set(CacheManager* self, const char* key, const char* value) {
if (!self || self->count >= 5) return;
pthread_mutex_lock(&self->lock);
// 检查是否已存在(简化处理:不覆盖,直接新增)
for (int i = 0; i < self->count; i++) {
if (strcmp(self->keys[i], key) == 0) {
pthread_mutex_unlock(&self->lock);
return;
}
}
// 添加新缓存
strncpy(self->keys[self->count], key, sizeof(self->keys[0])-1);
strncpy(self->values[self->count], value, sizeof(self->values[0])-1);
self->count++;
printf("Cache set: %s=%s\n", key, value);
pthread_mutex_unlock(&self->lock);
}
// 7. 接口实现:获取缓存
const char* cache_manager_get(CacheManager* self, const char* key) {
if (!self) return NULL;
pthread_mutex_lock(&self->lock);
for (int i = 0; i < self->count; i++) {
if (strcmp(self->keys[i], key) == 0) {
pthread_mutex_unlock(&self->lock);
return self->values[i];
}
}
pthread_mutex_unlock(&self->lock);
return "NOT_FOUND";
}
// 8. 接口实现:销毁实例
void cache_manager_destroy() {
pthread_mutex_lock(&init_lock);
if (instance) {
pthread_mutex_destroy(&instance->lock);
free(instance);
instance = NULL;
printf("CacheManager instance destroyed\n");
}
pthread_mutex_unlock(&init_lock);
pthread_mutex_destroy(&init_lock);
}
// 客户端使用:仅通过接口访问,不依赖结构体实现
int main() {
CacheManager* cache = cache_manager_get_instance();
// 设置缓存
cache_manager_set(cache, "user", "admin");
cache_manager_set(cache, "role", "root");
// 获取缓存
printf("Get user: %s\n", cache_manager_get(cache, "user"));
printf("Get role: %s\n", cache_manager_get(cache, "role"));
// 销毁实例
cache_manager_destroy();
return 0;
}
以上代码运行结果
CacheManager instance created
Cache set: user=admin
Cache set: role=root
Get user: admin
Get role: root
CacheManager instance destroyed
其的UML图为

即通过前向声明typedef struct CacheManager CacheManager隐藏结构体细节,外部仅能通过接口函数操作单例,符合 C 语言的模块化设计; 单例的创建、销毁和功能实现被封装在接口后,即使内部实现修改(如缓存大小调整),客户端代码也无需变动。
实例 5:线程安全的懒汉式单例(计数器服务)
使用 C11 的stdatomic库实现无锁线程安全,适合高性能场景(如全局计数器)。
c
#include <stdio.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <pthread.h>
// 1. 单例结构体:全局计数器服务
typedef struct {
atomic_uint count; // 原子计数器(线程安全)
} CounterService;
// 2. 静态全局实例指针(原子类型,确保并发访问安全)
static _Atomic(CounterService*) instance = NULL;
// 3. 全局访问函数:无锁线程安全(基于C11 atomic)
CounterService* counter_service_get_instance() {
// 首次读取实例(弱原子操作,开销低)
CounterService* tmp = atomic_load_explicit(&instance, memory_order_acquire);
if (!tmp) {
// 临时分配实例(仅在首次创建时执行)
CounterService* new_instance = malloc(sizeof(CounterService));
if (new_instance) {
atomic_init(&new_instance->count, 0); // 初始化计数器为0
}
// 原子比较交换:若instance仍为NULL,则设置为new_instance
if (!atomic_compare_exchange_strong_explicit(
&instance, &tmp, new_instance,
memory_order_release, memory_order_acquire)) {
// 交换失败:说明其他线程已创建实例,释放当前new_instance
free(new_instance);
tmp = atomic_load_explicit(&instance, memory_order_acquire);
}
printf("CounterService instance created\n");
}
return tmp;
}
// 4. 计数器功能:自增并返回新值
unsigned int counter_service_increment(CounterService* self) {
if (!self) return 0;
return atomic_fetch_add(&self->count, 1) + 1; // 原子自增
}
// 5. 销毁函数
void counter_service_destroy() {
CounterService* tmp = atomic_load_explicit(&instance, memory_order_acquire);
if (tmp) {
atomic_store_explicit(&instance, NULL, memory_order_release);
free(tmp);
printf("CounterService instance destroyed\n");
}
}
// 模拟多线程并发计数
#define THREAD_COUNT 5
void* thread_func(void* arg) {
CounterService* counter = counter_service_get_instance();
for (int i = 0; i < 1000; i++) {
counter_service_increment(counter);
}
return NULL;
}
int main() {
pthread_t threads[THREAD_COUNT];
// 启动多个线程并发计数
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_create(&threads[i], NULL, thread_func, NULL);
}
// 等待所有线程结束
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_join(threads[i], NULL);
}
// 验证计数结果(应为5*1000=5000)
CounterService* counter = counter_service_get_instance();
printf("Final count: %u\n", atomic_load(&counter->count));
counter_service_destroy();
return 0;
}
以上的代码运行结果为如下
CounterService instance created
Final count: 4000
CounterService instance destroyed
执行结果出现4000而非预期的5000,原因可能是:在counter_service_get_instance函数中,当多个线程同时进入if (!tmp)分支时,虽然atomic_compare_exchange_strong_explicit能保证最终只有一个线程成功创建实例,但其他线程在free(new_instance)后,会再次通过tmp = atomic_load_explicit获取实例。然而,这里的tmp在compare_exchange失败后已经被更新为 "其他线程创建的实例地址"(compare_exchange的第二个参数是输出型,会返回当前instance的值),因此后续的tmp = atomic_load_explicit可能是冗余的,但通常不会导致错误
修改代码
// 原子比较交换:若instance仍为NULL,则设置为new_instance
if (!atomic_compare_exchange_strong_explicit(
&instance, &tmp, new_instance,
memory_order_release, memory_order_acquire)) {
// 交换失败:说明其他线程已创建实例,释放当前new_instance
free(new_instance);
tmp = atomic_load_explicit(&instance, memory_order_acquire);
}
==>
// 原子比较交换:若instance仍为NULL,则设置为new_instance
if (!atomic_compare_exchange_strong_explicit(
&instance, &tmp, new_instance,
memory_order_release, memory_order_acquire)) {
// 交换失败:说明其他线程已创建实例,释放当前new_instance
free(new_instance);
}
else
{ tmp = new_instance;//atomic_load_explicit(&instance, memory_order_acquire);
}
这样重新编译后,执行结果为
CounterService instance created
Final count: 5000
CounterService instance destroye
即使用 C11 的_Atomic和原子操作(atomic_load、atomic_compare_exchange_strong)实现无锁线程安全,避免互斥锁的性能开销;适合高性能场景(如全局计数器),多个线程可并发调用counter_service_increment,通过原子操作保证计数准确性。
四、Linux 内核中的单例模式应用
- 进程 ID(PID)分配器 :内核中
pid_namespace的 PID 分配器是典型单例,每个命名空间内仅有一个 PID 管理器(struct pid_namespace),负责分配和回收 PID,确保同一命名空间内 PID 的唯一性。其全局访问点为task_active_pid_ns(current),返回当前进程所在的 PID 命名空间单例。 - 页分配器(Buddy System) :内核的伙伴系统是管理物理内存页的单例组件,系统启动时初始化(
buddy_init),全局仅有一个实例负责内存页的分配(__alloc_pages)和释放(__free_pages)。通过get_page_from_freelist等函数全局访问,确保物理内存管理的唯一性。 - 中断控制器(IRQ Controller) :对于特定硬件架构(如 ARM GIC),中断控制器是物理唯一的硬件资源,内核通过
struct irq_chip和irq_desc数组实现单例管理,全局仅有一套中断处理机制,通过request_irq、free_irq等接口访问,避免多实例导致的中断路由冲突。 - 系统时间管理器(jiffies) :内核中的
jiffies(节拍计数器)是全局唯一的时间戳,由定时器中断驱动自增,所有模块通过get_jiffies_64获取当前时间,本质是一个只读单例。其初始化在系统启动时完成,全程唯一且不可修改(除定时器中断)。 - VFS 超级块管理器 :虚拟文件系统(VFS)的超级块链表(
super_blocks)是单例管理结构,全局仅有一个链表记录所有已挂载文件系统的超级块,通过get_super、put_super等函数访问,确保文件系统挂载状态的全局一致性。
五、实现注意事项
- 线程安全是核心 :
- 懒汉式单例必须处理多线程并发创建问题,推荐使用 "双重检查锁定(DCL)+ 互斥锁" 或 C11 原子操作;
- 饿汉式单例在单线程初始化阶段(
main前)创建,天然线程安全,但需确保初始化逻辑无副作用(如依赖其他未初始化的全局变量)。
- 资源释放需谨慎 :
- 应用程序中,单例应提供
destroy函数,在程序退出前释放资源(如文件句柄、内存),避免资源泄漏; - 内核模块中,单例通常随模块卸载(
module_exit)释放,但需确保此时无其他模块正在使用单例(可通过引用计数控制)。
- 应用程序中,单例应提供
- 禁止复制与赋值 :
- C 语言中需通过
static和封装接口防止单例被复制(如不提供拷贝函数,将结构体定义为static),避免通过指针赋值创建新实例。
- C 语言中需通过
- 懒汉式 vs 饿汉式的选择 :
- 懒汉式:适合资源密集型组件(如大缓存),延迟创建节省资源,但需处理线程安全;
- 饿汉式:适合轻量、必用组件(如配置管理器),初始化简单且线程安全,但提前占用资源。
- 避免过度使用 :
- 单例本质是 "全局变量的封装",过度使用会导致代码耦合紧密、测试困难(全局状态难以隔离);仅在资源确实唯一且需全局共享时使用(如硬件控制器、全局计数器)。
六、补充说明
-
单例模式与全局变量的区别:
单例通过接口控制实例创建,隐藏实现细节,支持延迟初始化和线程安全,比直接使用全局变量更可控。全局变量在程序启动时初始化,无法延迟,且容易被意外修改。
-
单例的变体:
- 枚举单例:C 语言中可通过枚举 + 全局函数模拟(适合状态简单的单例),但功能有限;
- 嵌套单例:在单例中包含其他单例(如 "配置管理器" 包含 "日志管理器"),形成单例依赖树,需注意初始化顺序。
-
测试与单例:
单例的全局状态会导致单元测试困难(测试用例间状态污染),可通过 "依赖注入" 优化:在测试环境中替换单例实例为 mock 对象,避免依赖真实全局状态。
单例模式在 C 语言中(尤其是 Linux 内核)是保证资源唯一性的核心手段,通过合理使用可避免资源冲突、简化全局状态管理,但需严格控制线程安全和使用场景,防止过度设计导致的代码僵化。
|---------------------|
| 点击下面关注,获取最新最全分享,不迷路 |