c++单例模式和call_once函数

单例模式是一种常见的设计模式,用于确保某个类只能创建一个实例。由于单例模式是全局唯一的,因此在多线程中使用单例模式时需要考虑线程安全问题。

1.GetInstance()实例化一个对象
  • 懒汉式:第一次用到类的时候才会去实例化。

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。

  • 饿汉式:在类的加载的时候就去实例化

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创

#include<iostream>
#include<thread>
#include<mutex>
#include<string>


class Log {
public:
     
     Log() {};
     Log(const Log& log) = delete;
     Log& operator = (const Log& log) = delete;
     
     static Log& GetIntance(){

       static Log Log;   //饿汉模式,提前声明一个对象,需要的时候直接给。
       return Log; 
     }

/*****************************
     static Log& GetIntance(){
       
       static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。
       if(!Log) Log = new Log;
       return *Log;
     }
****************************/
     
};


void printfLog(std::string msg){

     std::cout<<msg<<std::endl;
}

void func(std::string msg){

     Log::GetInstance().printLog("error!!!");
}

int main(){
  
  std::thread t1(func);
  std::thread t1(func);
  t1.json;
  t2.json;

  Log::GetInstance().printLog("error!!!");
}

==================================

2.多线程出现线程安全问题

t1和t2同时调用func函数,func函数调用GetInstance函数实例化一个对象,但是如果t1和t2同时new一个对象Log,当new的Log对象的地址还没来得及赋值给Log,t2又new了一个对象,这时就会出现错误。

代码实例如下:

#include<iostream>
#include<thread>
#include<mutex>
#include<string>

class Log {
public:
     
     Log() {};
     Log(const Log& log) = delete;
     Log& operator = (const Log& log) = delete;
/*****************************     
     static Log& GetIntance(){
     static Log Log;   //饿汉模式,提前声明一个对象,需要的时候直接给。
     return Log; 
     }
****************************/

     static Log& GetIntance(){
       
       static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。
       if(!Log) Log = new Log;
       return *Log;
     }
    
void printfLog(std::string msg){

     std::cout<<msg<<std::endl;
}
     
};



    void func(std::string msg){

        Log::GetInstance().printLog("error!!!");
    }

int main(){
  
  std::thread t1(func);
  std::thread t1(func);
  t1.json;
  t2.json;

  Log::GetInstance().printLog("error!!!");
}

==================================

从性能线程上的区别:
1、线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。

懒汉式本身是非线程安全的。

2、资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内 存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

================================

3.call_once函数

在多线程中,有一种场景是某个任务只需要执行一次,可以用C++11中的std::call_once函数配合std::once_flag来实现。多个线程同时调用某个函数,std::call_once可以保证多个线程对该函数只调用一 次。

   void call_once (once_flag& flag, Fn&& fn, Args&&...args);

        第一个参数是std::once_flag的对象(once_flag是不允许修改的,其拷贝构造函数和operator=函数都声明为delete),第二个参数可调用实体,即要求只执行一次的代码,后面可变参数是其参数列表。

        call_once保证函数fn只被执行一次,如果有多个线程同时执行函数fn调用,则只有一个活动线程(active call)会执行函数,其他的线程在这个线程执行返回之前会处于”passive execution”(被动执行状态)——不会直接返回,直到活动线程对fn调用结束才返回。对于所有调用函数fn的并发线程,数据可见性都是同步的(一致的)。

        如果活动线程在执行fn时抛出异常,则会从处于”passive execution”状态的线程中挑一个线程成为活动线程继续执行fn,依此类推。一旦活动线程返回,所有”passive execution”状态的线程也返回,不会成为活动线程。(实际上once_flag相当于一个锁,使用它的线程都会在上面等待,只有一个线程允许执行。如果该线程抛出异常,那么从等待中的线程中选择一个,重复上面的流程)。
                        

代码实例如下:

#include<iostream>
#include<thread>
#include<mutex>
#include<string>

static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。
static std::once_flag once;
 
class Log {
public:
     
     Log() {};
     Log(const Log& log) = delete;
     Log& operator = (const Log& log) = delete;
     /*****************************
     static Log& GetIntance(){
       
       static Log *Log = nullptr;   //懒汉模式,需要的时候再去创建一个对象。
       if(!Log) Log = new Log;
       return *Log;
     }
     ****************************/     
     
     static Log& GetIntance(){

      std::call_once(once,init);
       return *Log; 
     }
};



static void init()
{
   if(!Log) Log = new Log;
}


void printfLog(std::string msg){

     std::cout<<msg<<std::endl;
}

void func(std::string msg){

     Log::GetInstance().printLog("error!!!");
}

int main(){
  
  std::thread t1(func);
  std::thread t1(func);
  t1.json;
  t2.json;

  Log::GetInstance().printLog("error!!!");
}

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肖爱Kun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值