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

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

posted @ 2014-10-10 13:16  (@_@)ゞ  阅读(1328)  评论(0编辑  收藏  举报