单一模式
单一模式是指在C语言中整个文件只有一个单一的结构体,这个结构体负责所有的结构体变量。
单例模式的特征
- 单例类,有且只有一个结构体类对象;
- 单例类必有一个函数用来创建自己的唯一对象。
- 其他对象都可以访问这个单例类创建的唯一对象。
先创建一个结构体类型,通过结构体类型定义出来结构体变量,在整个程序中,有且只有一个,其他函数可以通过特定的方法,获得这个结构体变量。
全局可访问有且只有一个的结构体表
什么情况下使用单例模式
保证程序全局只有一个结构体变量的时候,单例模式很好的解决了同一个结构体类型的变量被反复创建,造成内存浪费,
例如: UART,在多线程下每个外设在使用UART时都会进行初始化,然后造成资源浪费。
如何使用单例模式
创建一个结构体指针变量时,需要先判断这个结构体指针是否已经存在。也就是说,要判断这个结构体指针是否已经获得了内存空间,如果是,则直接返回该内存空间的指针,如果没有则malloc创建,并返回创建后的内存指针。
通常使用一个函数调用malloc来为该结构体指针分配内存(也可以用静态内存的方式进行创建,不用malloc)。
代码示例
- 创建一个single_mode.h 头文件,并在这个头文件声明一个串口类操作类,这个包含了串口设备的属性和操作方法。
#ifndef _SINGLE_MODE_H_
#define _SINGLE_MODE_H_
//定义一个串口类,串口类提供了三个串口操作方法
typedef struct uart_handler
{
/*串口设备属性*/
unsigned int baudrate; //波特率
unsigned char data_bit; //数据位
unsigned char stop_bit; //停止位
unsigned char parity; //校验位
/*串口设备的操作方法*/
void(*uart_init)(void); //串口初始化
int(*uart_read)(char*p_buff_read,int read_len); //串口读数据
int(*uart_write)(char *p_buff_write,int write_len); //串口写数据
}uart_handler_t;
#endif
- 然后在single_mode.c文件中,实现了串口的操作方法,主要是串口设备的初始化,串口设备的读操作和写造作。简化函数三个函数是打印功能,可以根据实际进行重写三个函数。
static void uart_init(void)
{
//通过打印的方式,表示初始话函数调用完成
printf("uart_init function call success");
}
static int uart_read(char *p_buff_read, int read_len)
{
printf("uart_read function call success");
return 1;
}
static int uart_write(char *p_buff_write, int write_len)
{
printf("uart_write function call success");
return 2;
}
- 单例模式在使用时候,可以通过一个函数来获取这个唯一的实例对象,如果这个对象没有被创建,则进行创建。如果对象已经被创建,则直接返回对象的指针。
//在这个函数里面,获取uart_handler_t的操作句柄,如果没有uart_handler_t构建的实例,则创建一个实例;如果有实例则直接返回指针。
uart_handler_t *get_uart_handler(void)
{
static uart_handler_t*uart_handler = NULL; //使用静态方式定一个实例句柄,并赋值为NULL
if(NULL == uart_handler) //如果实例句柄为空,也就是首次创建
{
uart_handler = (uart_handler_t*)malloc(sizeof(uart_handler_t));//分配内存
if(NULL != uart_handler)//分配内存成功
{
//对函数指针进行赋值
uart_handler->uart_init = uart_init;
uart_handler->uart_read = uart_read;
uart_handler->uart_write = uart_write;
}
else //内存空间分配失败
{
printf("uart_handler malloc fail");
}
}
return uart_handler;
}
- 在使用的时候,可以直接调用get_uart_handler来获取对象实例,获取后就可以通过这个实例来操作串口设备了,无论线程里面调用多少次,获取到的实例对象都是同一个东西。
int main(void)
{
char read_buff[256];
printf("single_mode start");
/*在不同的线程中,为了避免资源抢夺,这里应该加互斥对资源进行保护,申请获取互斥锁*/
//多次获取uart_handler对象,无论获取多少次,都不会重复使用*/
uart_handler_t *uart_handler_1 = get_uart_handler();
uart_handler_t *uart_handler_2 = get_uart_handler();
uart_handler_t *uart_handler_3 = get_uart_handler();
if((uart_handler_1 == uart_handler_2)&&(uart_handler_2 == uart_handler_3))
{
print("uart_handler all the same");
}
else
{
print("uart_handler are different");
}
//通过三个对象,分别调用不同的函数
uart_handler_1->uart_init();
uart_handler_2->uart_read(read_buff,sizeof(read_buff));
uart_handler_3->uart_write("hello",sizeof("hello"));
//释放互斥锁
return 0;
}
- 对象实例是在全局唯一,多个线程访问的时候,会存在资源竞争的问题,因此在使用这个对象实例的时候,要使用互斥锁进行线程安全处理。
- 确定程序的周期里面,不再使用这个对象实例,可以对其进行销毁操作。
//销毁uart_handler_t构建的实例
void destory_uart_handler(uart_handler_t *uart_handler)
{
if(NULL != uart_handler)
{
free(uart_handler);
uart_handler = NULL;
}
}