【知识分享】C语言中的设计模式——单例模式

背景

    在23种设计模式中,单例模式属于创建型模式,在面向对象设计里是最简单的一种设计模式。

名词释义

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。

C语言应用

    说白了就是有创建一个唯一变量实体,全局共享。

例子

    这个模式在单片机底层驱动会用得比较多,比如ST官方的HAL库,基本就是一个外设定义一个实体。下面我们就以串口通信为例,来看下这个模式有什么特点。
    根据创建实体的位置,这里分为懒汉式和饿汉式两种。
懒汉式
    顾名思义,很懒,只有在使用的时候才创建实体。建议在存在内存管理的系统中使用。

#include <stdint.h>
#include <stdlib.h>

/* 串口通信缓存大小 */
#define UART_BUFF_SIZE		255

/* 串口通信结构块 */
struct tagUartCB
{
    
    
	uint8_t Buff[UART_BUFF_SIZE];
	uint8_t Size;
};

/* 获取实体 */
struct tagUartCB* GetUartCB(void)
{
    
    
	static struct tagUartCB* uart_cb = NULL;
	
	if (NULL == uart_cb)
	{
    
    
		/* --此处可能被重入-- */
		uart_cb = malloc(sizeof(struct tagUartCB));
	}
	
	return uart_cb ;
}

饿汉式
    就如名字所说,饿汉,巴不得马上就可以使用,所以程序开始运行时即已创建实体。模块内部创建实体,对外只提供一个调用函数。

#include <stdint.h>
#include <stdlib.h>
/* 串口通信缓存大小 */
#define UART_BUFF_SIZE		255

/* 串口通信结构块 */
struct tagUartCB
{
    
    
	uint8_t Buff[UART_BUFF_SIZE];
	uint8_t Size;
};

/* 唯一实体 */
static struct tagUartCB UartCB;

/* 获取实体 */
struct tagUartCB* GetUartCB(void)
{
    
    
	return UartCB;
}

线程锁
    在使用懒汉式的时候,会存在一个问题,就是在多线程调用时,可能会导致重入,从而导致创建了多个实体,为了解决这个问题,这里可以加个线程锁。

#include <stdint.h>
#include <stdlib.h>

/* 串口通信缓存大小 */
#define UART_BUFF_SIZE		255

/* 串口通信结构块 */
struct tagUartCB
{
    
    
	uint8_t Buff[UART_BUFF_SIZE];
	uint8_t Size;
};

/* 获取实体 */
struct tagUartCB* GetUartCB(void)
{
    
    
	static struct tagUartCB* uart_cb = NULL;
	/* 禁止任务调度 */
	xTaskResumeAll();
	
	if (NULL == uart_cb)
	{
    
    
		uart_cb = malloc(sizeof(struct tagUartCB));
	}
	
	/* 启用任务调度 */
	vTaskSuspendAll();
	
	return uart_cb ;
}

    因为一直调用禁止任务调度会影响其他任务的运行时效性,所以为了不影响正常使用的性能,上面代码可以改也如下方式。

/* 获取实体 */
struct tagUartCB* GetUartCB(void)
{
    
    
	static struct tagUartCB* uart_cb = NULL;
	/* 已创建了实体就不禁止任务调度 */
	if (NULL == uart_cb)
	{
    
    
		/* 禁止任务调度 */
		xTaskResumeAll();
		
		if (NULL == uart_cb)
		{
    
    
			uart_cb = malloc(sizeof(struct tagUartCB));
		}
		
		/* 启用任务调度 */
		vTaskSuspendAll();
	}
	
	return uart_cb ;
}

适用范围

    一般在驱动中最为常见,比如现在有一个板子,上面有两个485通信,如果此时要做串口的通信缓存,那只要做两套缓存即可,即使逻辑层面上,两个485可以跟多个设备进行通信,但每个485同一时刻只能跟一个设备通信,即同一时刻只需要一套缓存,那么此时多个设备的通信缓存是可以共享的。这时候使用单例模式可以大量减少内存的消耗。

优势

  1. 节省Ram空间
  2. 资源共享,不需要考虑同步问题

劣势

  1. 共用全局资源,多线程使用时需要考虑重入的风险。

猜你喜欢

转载自blog.csdn.net/u012749085/article/details/126114029
今日推荐