Linux:线程安全的单例模式

设计模式

设计模式听上去是个很高贵的名词,其实就是是一套 多数人知晓、被反复使用、经过分类编目的、代码设计经验的总结,简称:对于编程比较典的场景的解决方案

单例模式

单例模式就是其中一种设计模式,是设计模式里的创建型模式(设计模式包含很多种)

单例模式:确保一个类只有一个实例,提供一个全局访问点来访问这个实例,并提供一个全局访问点来获取这个实例。

单例模式通常用于游戏额需要频繁创建和销毁同一对象的场景,单例模式可以减少系统性能开销。

举个例子:在一家火锅店,客人需要火锅调料可以是各种各样的,而商家不会设置很多个自助调料区分开放在不同的地方,而会把他们放在一起,这个所有调料集中的区域就是唯一的自助调料区

在这个栗子中:我们确保了只有一个自助调料区这个实例,提供唯一的位置(全局访问点)来访问这个实例,大家都可以随时、同时、同地访问,避免了资源的浪费

单例模式有两种分类:懒汉模式、饿汉模式

懒汉模式、饿汉模式

等你搞懂就被淘汰啦!

看看下面的代码:

复制代码
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> 
typedef struct {
    int value;
}Singleton;
/*
Singleton*getInstance(){
    static Singleton instance;
    return &instance;//直接返回,饿汉模式
}
*/

Singleton*getInstance(){
    static Singleton* instance=NULL;
    if(instance==NULL){//用到的时候才初始化,很懒
        instance=(Singleton*)malloc(sizeof(Singleton));
        instance->value=0;
    }
    return instance;
}
int main(){
    Singleton* p1=getInstance();
    Singleton* p2=getInstance();
    printf("p1==%p\n",p1);//p1==0x562d3adac2a0
                          //p2==0x562d3adac2a0
    printf("p2==%p\n",p2);//打印发现地址一样
    return 0;
}

#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> 
typedef struct {
    int value;
}Singleton;
/*
Singleton*getInstance(){
    static Singleton* instance=NULL;
    if(instance==NULL){//用到的时候才初始化,很懒
        instance=(Singleton*)malloc(sizeof(Singleton));
        instance->value=0;
    }
    return instance;
}
*/

Singleton*getInstance(){
    static Singleton instance;
    return &instance;//直接返回,饿汉模式
}

int main(){
    Singleton* p1=getInstance();
    Singleton* p2=getInstance();
    printf("p1==%p\n",p1);//p1==0x563ce8622014
                          //p2==0x563ce8622014
    printf("p2==%p\n",p2);//打印发现地址一样
    return 0;
}

首先在打印的时候我们可以看出p1和p2的地址是一样的,说明他们是同一个实例,符合单例模式

其次我们来看两者的区别:

饿汉模式是很饥饿,程序启动时就实例化;懒汉模式如其名,只有需要的时候才会创建实例

吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭

吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式

关于线程安全

饿汉模式因为每次都只涉及到读操作,所以不会引发线程安全问题;但是懒汉模式因为涉及到了读写操作,就为线程安全问题的产生埋下了隐患。

由于线程调度的随机性,当两个线程在同一时间调用该方法时,错落的执行顺序可能导致if语句出现不可避免的错判,进而导致最终创建了两个SingletonLazy实例。

偷的图(java写的),大概就是这个意思

①T1线程执行完if语句,因为第一次调用getIntance方法,intance==null,所以T1线程接下来将要创建SingletonLazy实例,并将其赋值给intance。

②轮到T2线程执行,由于T1线程中尚未进行实例创建,此时仍旧是instance==null,所以if语句判断通过。接下来创建实例、赋值一气呵成,最后还将创建的Singleton对象返回。

③再次轮到T1线程,继续执行,创建了一个和T2线程不同的Singleton实例,引用赋给instance。最后,这个引用又被返回。

综上所述,在多线程情况下竟然出现了两个懒汉实例,这不符合单例模式下一个类只能创建一个实例的原则,很可能产生无法预估的错误,妥妥的bug代码。所以单线程下实现的懒汉模式不是线程安全的。

我们可以引入锁:

复制代码
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> 
typedef struct {
    int value;
}Singleton;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

Singleton*getInstance(){
    static Singleton* instance=NULL;
    if(instance==NULL){//用到的时候才初始化,很懒
        pthread_mutex_lock(&mutex);
        if(instance==NULL){//再判断一次,因为多线程情况下可能由于锁竞争陷入阻塞,所以其他线程可能创建过实例了
        instance=(Singleton*)malloc(sizeof(Singleton));
        instance->value=0;
        pthread_mutex_unlock(&mutex);
        }
    }
    return instance;
}
/*
Singleton*getInstance(){
    static Singleton instance;
    return &instance;//直接返回,饿汉模式
}
*/


int main(){
    Singleton* p1=getInstance();
    Singleton* p2=getInstance();
    printf("p1==%p\n",p1);//p1==0x562d3adac2a0
                          //p2==0x562d3adac2a0
    printf("p2==%p\n",p2);//打印发现地址一样
    return 0;
}

这样就会克服线程随机调度问题

短短的一篇~万圣节快乐捏(金工实习好累)

相关推荐
yuanmenghao4 分钟前
Linux 性能实战 | 第 7 篇 CPU 核心负载与调度器概念
linux·网络·性能优化·unix
qq_2975746717 分钟前
Linux 服务器 Java 开发环境搭建保姆级教程
java·linux·服务器
70asunflower42 分钟前
Emulation,Simulation,Virtualization,Imitation 的区别?
linux·docker
神梦流1 小时前
GE 引擎的内存优化终局:静态生命周期分析指导下的内存分配与复用策略
linux·运维·服务器
凡人叶枫1 小时前
C++中输入、输出和文件操作详解(Linux实战版)| 从基础到项目落地,避坑指南
linux·服务器·c语言·开发语言·c++
wdfk_prog2 小时前
[Linux]学习笔记系列 -- [drivers][input]serio
linux·笔记·学习
xuhe22 小时前
[全流程详细教程]Docker部署ClawBot, 使用GLM4.7, 接入TG Bot实现私人助理. 解决Docker Openclaw Permission Denied问题
linux·docker·ai·github·tldr
Lsir10110_2 小时前
【Linux】进程信号(下半)
linux·运维·服务器
酉鬼女又兒2 小时前
零基础入门Linux指南:每天一个Linux命令_pwd
linux·运维·服务器
云飞云共享云桌面2 小时前
高性能图形工作站的资源如何共享给10个SolidWorks研发设计用
linux·运维·服务器·前端·网络·数据库·人工智能