当前位置: 首页 > article >正文

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;
}

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

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


http://www.kler.cn/a/379613.html

相关文章:

  • 互联网大厂钟爱的压测工具分享,战绩可查!
  • <项目代码>YOLOv8 猫狗识别<目标检测>
  • Android:ViewPaper动态添加移除第一页
  • 「Mac畅玩鸿蒙与硬件17」鸿蒙UI组件篇7 - Animation 组件基础
  • Java毕业设计-基于微信小程序的校园二手物品交易系统的实现(V2.0)
  • 【Linux】——如何安装g++
  • 进程的概念
  • Vue学习之路16----pinia
  • 家具产品的耐用性新标准,矫平机为家具制造提供新保障
  • SQL中`ORDER BY`、`SORT BY`、`DISTRIBUTE BY`、`GROUP BY`、`CLUSTER BY`的区别详解
  • 什么是严肃游戏,严肃游戏本地化的特点是什么?
  • 【C语言刷力扣】3216.交换后字典序最小的字符串
  • 第十五章 Vue工程化开发及Vue CLI脚手架
  • 贪心算法理论基础和习题【算法学习day.17】
  • Python代码解析:问题分类器实现
  • el-table type=“selection“换页多选数据丢失的解决办法
  • dify实战案例分享-基于多模态模型的发票识别
  • git submodule
  • 【AIGC】深入探索『后退一步』提示技巧:激发ChatGPT的智慧潜力
  • 【jvm】对象分配过程
  • PostgreSQL JOIN 操作深入解析
  • 《星光予你》系列网剧正式开机! “黑莲花”陷入时间循环攻略疯批霸总
  • 报错 sys_platform == “win32“ (from mmcv) (from versions: none)
  • excel表格文字识别-ocr表格文字提取api接口集成-python
  • 双向链表专题
  • word选择题转excel(一键转写,无格式要求)