• C++——单例模式的DCLP(双重锁)实现以及性能测评


    单例模式的描述是: 确保一个类只有一个实例,并提供对该实例的全局访问。

    从这段话,我们可以知道,单例模式的最重要特点就是:一个类最多只有一个对象。

    对于一个普通类,我么可以生成任意对象,我们为了避免生成太多的类,需要将类的构造函数设为私有

    这样的话,我们为了获取实例,只能借助于类的内部函数,而且必须是static函数(非static函数中均包含一个隐式参数this,由于我们没办法实例化,所以只能通过static函数来获取实例):

     1 class Singleton
     2 {
     3 public:
     4     static Singleton *getInstance()
     5     {
     6         return new Singleton;
     7     }
     8 private:
     9     Singleton() { }
    10 };

    这样虽然可以生成对象了,但每次都去new,无法保证唯一性,所以我们将对象保存在一个static 指针中:

     1 class Singleton
     2 {
     3 public:
     4     static Singleton *getInstance()
     5     {
     6         if(pInstance_ == NULL) //线程的切换
     7         {
     8             ::sleep(1);
     9             pInstance_ = new Singleton;
    10         }
    11             
    12         return pInstance_;
    13     }
    14 private:
    15     Singleton() { }
    16 
    17     static Singleton *pInstance_;
    18 };
    19 
    20 Singleton *Singleton::pInstance_ = NULL;

    这样我们每次获取对象时,都会检查该指针是否为空。

    这样虽然在单线程中可以通过测试,但在多线程中,由于线程的切换,我们生成的对象将不唯一。

    所以,我们需要通过加锁来解决这个问题:

     1 class Singleton
     2 {
     3 public:
     4     static Singleton *getInstance()
     5     {
     6         mutex_.lock();
     7         if(pInstance_ == NULL) //线程的切换
     8             pInstance_ = new Singleton;
     9         mutex_.unlock();
    10         return pInstance_;
    11     }
    12 private:
    13     Singleton() { }
    14 
    15     static Singleton *pInstance_;
    16     static MutexLock mutex_;
    17 };
    18 
    19 Singleton *Singleton::pInstance_ = NULL;
    20 MutexLock Singleton::mutex_;

    加锁后,虽然解决了这种问题,可是互斥锁会极大的降低系统的并发能力,因为每次调用都要加锁。

    测试代码如下:

     1 class TestThread : public Thread
     2 {
     3 public:
     4     void run()
     5     {
     6         const int kCount = 1000 * 1000;
     7         for(int ix = 0; ix != kCount; ++ix)
     8         {
     9             Singleton::getInstance();
    10         }
    11     }
    12 };
    13 
    14 int64_t getUTime()
    15 {
    16     struct timeval tv;
    17     ::memset(&tv, 0, sizeof tv);
    18     if(gettimeofday(&tv, NULL) == -1)
    19     {
    20         perror("gettimeofday");
    21         exit(EXIT_FAILURE);
    22     }
    23     int64_t current = tv.tv_usec;
    24     current += tv.tv_sec * 1000 * 1000;
    25     return current;
    26 }
    27 
    28 int main(int argc, char const *argv[])
    29 {
    30     //Singleton s; ERROR
    31 
    32     int64_t startTime = getUTime();
    33 
    34     const int KSize = 100;
    35     TestThread threads[KSize];
    36     for(int ix = 0; ix != KSize; ++ix)
    37     {
    38         threads[ix].start();
    39     }
    40 
    41     for(int ix = 0; ix != KSize; ++ix)
    42     {
    43         threads[ix].join();
    44     }
    45 
    46     int64_t endTime = getUTime();
    47 
    48     int64_t diffTime = endTime - startTime;
    49     cout << "cost : " << diffTime / 1000 << " ms" << endl;
    50 
    51     return 0;
    52 }
    View Code

    测试结果如下:

    cost : 7304 ms

    我们可以采用双重锁模式来提高性能。

     1 class Singleton
     2 {
     3 public:
     4     static Singleton *getInstance()
     5     {
     6         if(pInstance_ == NULL)
     7         {
     8             mutex_.lock();
     9             if(pInstance_ == NULL) //线程的切换
    10                 pInstance_ = new Singleton;
    11             mutex_.unlock();
    12         }
    13 
    14         return pInstance_;
    15     }
    16 private:
    17     Singleton() { }
    18 
    19     static Singleton *pInstance_;
    20     static MutexLock mutex_;
    21 };
    22 
    23 Singleton *Singleton::pInstance_ = NULL;
    24 MutexLock Singleton::mutex_;

    我们在getInstance中采用了双重检查模式,这样做的优点为:

    内部采用互斥锁,代码无论如何是可靠的

    new出第一个实例后,后面每个线程访问到最外面的if判断就直接返回了,没有加锁的开销

    再次测试,结果为:

    cost : 486 ms

    这样,就简单的完成了对单例模式的一点性能改进。

  • 相关阅读:
    javascript DOM事件总结
    MySQL索引优化实例说明
    CSV导出大量数据
    最详细的PHP flush()与ob
    XSS攻击(跨站攻击)
    MySQL视图
    MySQL索引
    待整理
    Height、clientHeight、scrollHeight、offsetHeight 、scrollTop、offsetTop
    Cookie和Session的区别
  • 原文地址:https://www.cnblogs.com/gjn135120/p/4015633.html
Copyright © 2020-2023  润新知