C++ 线程安全的单例模式

本文详细介绍了单例模式的两种实现方式:懒汉模式和饿汉模式。懒汉模式在首次请求时创建实例,并通过双重检查锁定确保线程安全。饿汉模式则在程序启动时即创建实例,利用静态初始化保证线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

废话不多说,常用的代码积淀下来。

一、懒汉模式:即第一次调用该类实例的时候才产生一个新的该类实例,并在以后仅返回此实例。

需要用锁,来保证其线程安全性:原因:多个线程可能进入判断是否已经存在实例的if语句,从而non thread safety.

使用double-check来保证thread safety.但是如果处理大量数据时,该锁才成为严重的性能瓶颈。

1、静态成员实例的懒汉模式:

复制代码
 1 class Singleton
 2 {
 3 private:
 4     static Singleton* m_instance;
 5     Singleton(){}
 6 public:
 7     static Singleton* getInstance();
 8 };
 9 
10 Singleton* Singleton::getInstance()
11 {
12     if(NULL == m_instance)
13     {
14         Lock();//借用其它类来实现,如boost
15         if(NULL == m_instance)
16         {
17             m_instance = new Singleton;
18         }
19         UnLock();
20     }
21     return m_instance;
22 }
复制代码

2、内部静态实例的懒汉模式

这里需要注意的是,C++0X以后,要求编译器保证内部静态变量的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。

复制代码
 1 class SingletonInside
 2 {
 3 private:
 4     SingletonInside(){}
 5 public:
 6     static SingletonInside* getInstance()
 7     {
 8         Lock(); // not needed after C++0x
 9         static SingletonInside instance;
10         UnLock(); // not needed after C++0x
11         return instance; 
12     }
13 };
复制代码

二、饿汉模式:即无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。

由静态初始化实例保证其线程安全性,WHY?因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。

故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。

复制代码
 1 class SingletonStatic
 2 {
 3 private:
 4     static const SingletonStatic* m_instance;
 5     SingletonStatic(){}
 6 public:
 7     static const SingletonStatic* getInstance()
 8     {
 9         return m_instance;
10     }
11 };
12 
13 //外部初始化 before invoke main
14 const SingletonStatic* SingletonStatic::m_instance = new SingletonStatic;
复制代码

(完)

  
#1楼   2012-12-21 16:05  xjb_221   
  
#2楼   2013-11-20 20:59  果冻想   
谢谢楼主的分享;看了楼主的文章,让小弟地壶灌顶啊。才知道自己的渺小。

对于楼主的这篇文章,由于小弟学艺不精,有几个地方看不懂~希望能得到楼主的指教。提前谢过了。

在“二、饿汉模式”中,SingletonStatic类的构造函数已经是private的了,怎么还可以在外部进行new实例化操作,楼主的代码,小弟已经运行过了,确实没有错误,但是小弟着实不懂,希望得到你的解答。谢谢。
  
#3楼   2013-11-29 15:31  Moondark   
@ 宁采臣
路过,楼主的文章的确不错,对于你的问题,可以这样解答:private是类外不能访问的,但你看上述,它是通过类内元素m_instance进行new操作的,所以能够成功。

对于为何这里要采用const static,表示不是很理解
  
#4楼 [ 楼主2013-12-04 11:47  Jone Zhang   
@ 宁采臣
因为这个new实例化不是new对象,而是初始化(initialization),C++标准规定,非整型和枚举型静态常量类成员需要在类外进行初始化,就是说假如你写成:
private:
static const SingletonStatic* m_instance = new SingletonStatic;
编译是不成功的,因为这类声明只是个“声明式”(declaration)
,而不是“定义式”(definition),你尝试在类声明中进行初始化的话,编译不知道把这个对象初始化到哪去。
但是假如你的静态常量类成员是整型或枚举型的,却可以这样做,如:
private:
static const int someInt = 2;
为什么整型或枚举型可以,这是编译器层面的东西了,可以简单理解为因为这个静态常量类成员是一个整数,可以直接表示为机器数(当然是常量)。
这么说希望你能明白。
  
#5楼 [ 楼主2013-12-04 11:53  Jone Zhang   
@ Moondark
const和static都是必须的

const是保证这个单例对象不会被修改,
static是保证这个单例对象在程序开始时进入主函数之前就被初始化了,而且是线程安全的。

都是出于安全性的需求
  
#6楼   2014-02-24 15:58  eksay   
const SingletonStatic* SingletonStatic::m_instance = new SingletonStatic;

作为初始化的理解很受用,好文章。
  
#7楼   2014-03-12 11:43  stupidguy   
3 private:
static const SingletonStatic* m_instance;
5 SingletonStatic(){}
6 public:
static SingletonStatic* getInstance()
8 {
9 return m_instance;
10 }


将一个常量变成变量返回,你不觉得会编译不过吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值