`

C++多线程的Singleton(1)

阅读更多

C++多线程的Singleton(2)

 

Singleton是我使用过的最多的设计模式,也是日常工作中大家会经常用到的设计模式。其实,在C++里面写一个Singleton,不是一件非常容易的事情,以至于《C++设计新思维》里面花了一章内容专门讲解。难点在哪里呢?其实就是两个:

(1)多线程的并发性 (如果你的程序是单线程的,那么就没有这个问题)

(2)生命周期

 

下面我们就开始这个旅程。另外一点,我现在使用C++的原则是KISS,保持设计和代码的简单性。

 

首先,我们先来看看这两个问题。

(1)多线程的并发性,这不难理解。如果在单线程模式下,我们通常会这么写Singleton:

// Singleton.h
class Singleton
{
  public:
     ~Singleton(){}
     static Singleton& getInstance();

  private:
     Singleton(){}
     
};

// Singleton.cpp

/* static */
Singleton& Singleton::getInstance()
{
  static Singleton *instance = new Singleton;
  return &instance;
} 

  

 这种方法是按照《Effective C++》里面的建议,不使用class static变量,而使用函数static变量。这个是延迟初始化,如果整个过程没有函数调用Singleton::getInstance(),那么就不会有Singleton这个对象生成。为什么返回一个Reference,而不是一个指针,是因为这样不容易被误delete。可是如果是多线程呢?有Java经验的同学都会想到下面这个方法:

 

// Singleton.h 错误的实例
class Singleton
{
  public:
     ~Singleton(){}
     static Singleton& getInstance();

  private:
     Singleton(){}
     static Lock lock_;
};

// Singleton.cpp

/* static */
Lock Singleton::lock_;

Singleton& Singleton::getInstance()
{
  static Singleton *instance = NULL;
  if (NULL == instance) // thread B Pos1
  {
    LockHandler handler(lock_);  
    if (NULL == instance)
    {
      instance = new Singleton;  // thread A   Pos2
    }
  }
  return &instance;
} 

 (注解:Lock是类似与封装了pthread_mutex_t的类,具有lock(), unlock(), trylock()这样的方法;而LockHandler的构造函数就会调用lock_.lock(),析构函数会调用lock_.unlock(),这是一种RIIA的惯用法)。这段代码非常Java的Singleton代码,它有一个响亮的名字:两次检查。

 

public class Singleton {
    private Singleton(){}
    private static Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
          synchronized(Singleton.class) {
            if (instance == null) {
              instance = new Singleton();  
            }
          }
        }
    }
}

上面这段Java代码在Java5之后是正确的,在Java5之前,在有些平台上这段代码是有bug的。那C++的两次检查呢?是错误的。知道为什么这段代码是错误的,只会让让你很郁闷,因为instance = new Singleton这句话,根据正常的理解,应该是Singleton分好内存,然后再调用构造函数,然后再把那个地址赋值给instance。可是事实上这是不一定的,有可能先完成这次赋值,再调用构造函数。也许你应该明白为什么有问题了吧。假设threadA到了pos2那个地方,此时threadB刚好来到pos1,然后它发现instance==null不成立,于是threadB就可以使用instance了。可是此时,instance指向的对象还没有正常的调用构造函数。换句话说,threadB使用了一个没有被正确初始化的对象。看到这里也许你要哭了,没错,我也哭了。这不是我们程序员本需要考虑的问题,可是它就是像座山一样在那里。没办法,如果你使用C++,那么请接受它吧。我可以说这种问题很难测试到,也许它会跟随你的系统几个月甚至上年,可是有一天它突然崩溃了,你才知道原来是这样造成的。没办法,那我们退回到一个保守的地方:

 

// Singleton.h
class Singleton
{
  public:
     ~Singleton(){}
     static Singleton& getInstance();

  private:
     Singleton(){}
     static Lock lock_;
};

// Singleton.cpp

/* static */
Lock Singleton::lock_;

Singleton& Singleton::getInstance()
{
  static Singleton *instance = NULL;
  LockHandler handler(lock_); 
  if (NULL == instance) // thread B Pos1
  {
    instance = new Singleton;  // thread A   Pos2
  }
  return &instance;
} 

 这个代码是对的,正确的。可是它有些效率问题,因为你每次访问这个资源的时候,都需要去竞争这个lock_。我们需要继续优化这个。(未完待续)

 

0
1
分享到:
评论
1 楼 lovedame 2013-04-28  
// Singleton.h 错误的实例  
class Singleton  
{  
  public:  
     ~Singleton(){}  
     static Singleton& getInstance();  
  
  private:  
     Singleton(){}  
     static Lock lock_;  
};  
  
// Singleton.cpp  
  
/* static */  
Lock Singleton::lock_;  
  
Singleton& Singleton::getInstance()  
{  
  static Singleton *instance = NULL;  
  if (NULL == instance) // thread B Pos1  
  {  
    LockHandler handler(lock_);    
    if (NULL == instance)  
    { 
//改动之处------------------------------------------改动之处 
      Singleton *newInstance = new Singleton;
      instance = newSingleton;  // thread A   Pos2  
    }  
  }  
  return &instance;  
}   


楼主看这样可以吗 没在多线程下测试

相关推荐

    C++完美实现Singleton模式

    1. **线程安全**:在多线程环境下,如果多个线程同时尝试创建Singleton实例,可能会导致创建多个对象。为了保证线程安全,通常需要在`Instance()`方法中添加互斥锁(`mutex`)或者使用原子操作(`std::atomic`)。...

    C++ 实现的singleton 模式

    在多线程环境中,如果没有适当的同步机制,`getInstance()`可能会在不同线程中同时创建多个实例。在C++11及更高版本中,我们可以使用`std::call_once`和`std::once_flag`来确保实例只在第一次调用时创建: ```cpp #...

    C++中多线程与Singleton的那些事儿

    前言  前段时间在网上看到了个的面试题,大概...本文主要将从基本的单线程中的Singleton开始,慢慢讲述多线程与Singleton的那些事。  单线程  在单线程下,下面这个是常见的写法: template class Singleton

    使用C++11实现线程安全的单例模式

    在C++11之前,C++标准并不直接支持多线程编程。C++11引入了 `<thread>` 头文件,提供了线程创建、同步等机制,使开发者能够编写多线程程序。`std::mutex` 是一个互斥锁,用于保护共享资源,确保同一时间只有一个线程...

    c++线程安全日志类

    1. **线程安全**:在多线程环境中,多个线程可能会同时访问和修改同一个资源,这可能导致数据不一致或竞态条件。为了解决这个问题,我们的日志类应该实现线程安全的写入操作。这通常通过互斥锁(mutex)或原子操作...

    C++类中创建多线程实现本地和远程打印

    1. **多线程**:C++11及更高版本引入了`std::thread`库来支持线程创建。在`client.cpp`和`server.cpp`中,可能使用`std::thread`来创建新线程处理打印任务。线程可以独立于主线程运行,从而允许程序同时进行本地和...

    Linux多线程服务端编程:使用muduo C++网络库

    第1部分C++ 多线程系统编程 第1章线程安全的对象生命期管理3 1.1当析构函数遇到多线程. . . . . . . . . . . . . . . . .. . . . . . . . . . . 3 1.1.1线程安全的定义. . . . . . . . . . . . . . . . .. . . . . ....

    单例实现源码singleton-C++

    总结来说,C++中的单例模式实现多种多样,选择哪种方式取决于具体的应用场景,如是否考虑多线程、内存占用、初始化时机等。通过理解这些实现方式,我们可以更好地设计和使用单例模式,以满足软件的高效、稳定和灵活...

    17_懒汉式多线程同步优化.zip_C++

    在C++编程中,多线程同步是一个关键的议题,特别是在设计高效的并发程序时。"懒汉式"多线程同步是一种延迟初始化的策略,它旨在避免不必要的资源消耗,直到这些资源真正被需要时才进行初始化。这个"17_懒汉式多线程...

    设计模式C++学习之单例模式(Singleton)

    但这种方式在多线程环境下可能存在问题,因为`instance`的初始化不是原子操作,可能导致多个线程同时创建对象。 为了解决这个问题,可以采用双重检查锁定(Double-Checked Locking)的策略: ```cpp class ...

    C++中实现Singleton的正确方法

    在本例中,`singleton2_t`的静态实例在`singleton1_t`之前被初始化,导致`singleton1_t`的`count_`在`singleton2_t`构造时被改变,从而产生了错误的结果。 正确的Singleton实现应考虑以下几点: 1. **线程安全**:...

    详解C++实现线程安全的单例模式

    总结来说,C++实现线程安全的单例模式通常涉及到对实例化过程的控制,以确保在多线程环境下只有一个实例存在。饿汉模式在类加载时完成实例化,是线程安全的,而懒汉模式需要额外的同步机制如互斥锁来保证线程安全。...

    C++CLI中实现singleton模式

    双重检测锁(Double-Checked Locking)实现的Singleton模式在多线程应用中有相当的价值。在ACE的实现中就大量使用ACE_Singleton模板类将普通类转换成具有Singleton行为的类。这种方式很好地消除了一些重复代码臭味,...

    Singleton模式源程序

    Singleton模式是一种设计模式,它是创建型模式的一种,用于控制类的实例化过程,确保一个类在整个应用程序中只有一个实例存在。这种模式在系统中需要频繁创建和...同时,这也是一次了解和实践C++多线程编程的好机会。

    Java的Singleton模式代码(免资源分)

    1. **非线程安全**:这个版本在多线程环境下存在安全隐患,多个线程可能会同时进入`if`语句块创建多个实例。 2. **懒加载**:实现了懒加载机制,但没有解决线程安全问题。 3. **简单性**:相对于双重检查锁定,该...

    singleton单例模式

    2. **双重检查锁定(Double-Checked Locking)**:这种方法是在多线程环境下保证线程安全的单例。首先检查是否已经实例化,如果未实例化,则同步锁定构造函数,确保只有一个线程可以进入,然后创建实例。这种方式...

    C++实现的单例模式代码

    下面将详细探讨C++中实现单例模式的三种方法,以及在多线程环境下的考虑。 1. 静态成员变量法(单线程) 这是最简单的单例实现方式,通过类内部定义一个静态私有实例和公有的静态获取实例的方法。如下所示: ```...

    C++设计模式课件12_Singleton_单件模式.pdf

    1. **难以实现线程安全:** 在多线程环境下,如果没有正确处理同步问题,可能会导致多个实例被创建。 2. **违背单一职责原则:** 单例模式将对象的创建与对象的功能混为一谈,使得类承担了过多的职责。 3. **隐藏了...

    linux下线程安全的singleton模板

    在Linux环境下,线程安全的Singleton模板是一种至关...总之,设计线程安全的Singleton模板是C++多线程编程中的一个重要挑战。理解这些实现策略并根据实际情况选择最合适的方案,能够帮助我们编写出高效且可靠的代码。

Global site tag (gtag.js) - Google Analytics