首页 > 代码库 > C++——单例模式的DCLP(双重锁)实现以及性能测评

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

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

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