(载)对于c++中实现单例的讨论

C++中实现单例,需要注意两个问题:1.对内存的释放。2.多线程下的健壮性。

以下是CSDN上对于此问题的讨论,先存一个。

 
#9楼 得分:5回复于:2008-02-18 20:26:40
你这个方法实现singleton不太好,会有多线程同步问题。
比如说一开始Singleton::instance为空。然后有线程A和线程B同时访问Singleton::GetInstance()。
有意思的是当任务A执行if (NULL == instance)这句时,刚判断好instance确实为空想调用new,或者时间可以再放款到正在执行new操作,但是还没有返回;要命的是调度器由于某些原因一下子剥夺了线程A的执行又去执行线程B了。线程B顺利地创建了一个instance实例,然后在某一时刻被调度器剥夺,再次执行线程A。OK,此时线程A从刚才的混沌中继续执行它的new Singleton()的操作,这样线程A所创建出来的就是另一个实例了。此时Singleton宣告破灭。

 

因此,能够迎合多线程,乃至多处理的单件处理可以用以下方式:

C/C++ code
 class Singleton  
{  
 private:  
   static Singleton instance;  
  Singleton(void) {}  
 public:  
  static Singleton& GetInstance()  
  {
     return instance;
  }
};
Singleton Singleton::instance;

由于以上代码中,Singleton::instance在程序加载后由初始化程序建立,因此对于应用程序不会有多线程困扰。如果是在多处理器或多核处理器环境下还要注意Singleton的成员函数中的一些同步。

 
 
 
#10楼 得分:0回复于:2008-02-18 20:35:14
回复Jim_King_2000:

 

在C++中使用singleton不用new主要不是因为要担心如何delete。
如果你的应用程序指明了一个接收到一个消息,由专门一个任务相应析构这个singleton实例,可以显式地调用其析构函数:Singleton::GetInstance().~Singleton();
个人认为多任务机制下,用楼主的方法不太合适。而Jim_King_2000你的方法也有多线程问题。
因为局部static也不会对实例创建的操作原子化,除非构造函数内部具有锁机制。或者也是通过某种消息相应或通过状态表示要创建一个Singleton实例,以确保在第一次调用Singleton::GetInstance()时只有一个任务执行,且操作过程中不被打断,至少被打断后不会被重入。

 
 
 
#14楼 得分:0回复于:2008-02-19 08:40:39
感谢各位的精彩回复,让在下获益匪浅啊。

 

liuworld说得确实是,静态成员变量必须要在具体的cpp文件中进行定义,类中出现的静态成员变量只表示一种声明。

感谢zenny-chen的提醒,我这种写法在多线程环境下确实会有潜在问题,这是一种“恶汉式”的单例模式,就是需要的时候才创建实例,你提供的实现是一种“懒汉式”的单例模式,就是程序初始化加载的时候就首先创建好实例,然后每次引用即可。这确实可以解决一些多线程调度的问题,但是会提前占用系统资源,当然,一般来说,“恶汉式”的单例模式已经基本上可以满足我们的需求了。

对于“懒汉式”的单例模式,可以使用锁和双重判断的方式来避免重复创建的问题。

例如:
  static Singleton *GetInstance()   
  {   
  if (NULL == instance)   
  {   
  lock()
  {   
  if (instance == NULL)   
  instance = new Singleton();   
  }  
  }   
  return instance;   
  }

 
 
 
#15楼 得分:0回复于:2008-02-19 08:56:27
还是去看《Modern C++ Design》吧。
另外,google "Pattern Hatching",等,纯C、C++语言是无法安全实现多线程安全的singleton的。
勉强够用的实现就可以了。
 
 
 
#16楼 得分:0回复于:2008-02-19 11:08:55
to zenny_chen:
你的方法有效率问题。只要使用了你的类,这个静态对象都会被创建,无论该对象是否被用户使用。如果该对象的构造和析构十分费时的话,这样的方法会使效率大大降低。况且你的方法也并不能保证多线程安全。你怎么知道静态的instance在构造的时候没有别的线程正在调用GetInstance函数?

 

===========================================================================
如果你的应用程序指明了一个接收到一个消息,由专门一个任务相应析构这个singleton实例,可以显式地调用其析构函数:Singleton::GetInstance().~Singleton();  
===========================================================================
如果应用程序没有用消息怎么办?就算使用了消息,你怎么知道其它线程不需要Singleton对象了?万一程序非正常退出怎么办(比如遇到未处理的异常或者非法操作)?

===========================================================================
而Jim_King_2000你的方法也有多线程问题。  
===========================================================================
1、我真的很希望这是我的方法。可惜现实不是这样。这个方法属于Scott Meyers。
2、这个方法是实现singleton的基础。很多种singleton实现(如相互依赖的singleton,多线程下的singleton等等)都是依据这个方法实现的。
3、很多实现在一开始的时候并不考虑多线程。等到单线程的实现方法出来以后,再扩展到多线程上面或者由用户自行加锁。STL也并不是多线程的。但这并不影响使用,你自己加锁就是了。

所以说singleton里面不用new并不是因为多线程。而是没有必要用new。建议你也翻翻《Modern C++ Design》。里面有多线程singleton的实现。

 
 
 
#17楼 得分:0回复于:2008-02-19 20:19:58
To 楼上:

 

呵呵,在大多数场合我要让应用程序一直使用这个Singleton实例。因此,我将它放在全局数据存储区是个不错的主意,尤其在嵌入式系统中甚至可以将它放入ROM中。因此,在嵌入式系统中,这个实例在机器引导后就存在了,直到复位或关机。所以你说我还要对它在程序运行时进行创建或析构就显得荒谬了。

而本人没那么多时间去读那些号称为C++大师们写的东西,我感兴趣的部分是基于多核处理器的操作系统以及并行计算,和人工智能等问题。

 
 
 
#18楼 得分:0回复于:2008-02-20 13:53:25
to 楼上:
==========================================
在大多数场合我要让应用程序一直使用这个Singleton实例
==========================================
我们不应该假设这样的场合。你的assumption不具有通用性。如果用户的程序需要运行时创建singleton或者根本不创建某个singleton对象会怎样?如果用户只需要创建几个不同的singleton对象中的一个或几个(非全部)会怎么样?

 

==========================================
尤其在嵌入式系统中甚至可以将它放入ROM中
==========================================
同样,这仍然不具有通用性。Singleton是可以根据需要创建的,这样会大大限制了singleton的使用场合。

==========================================
因此,在嵌入式系统中,这个实例在机器引导后就存在了,直到复位或关机。所以你说我还要对它在程序运行时进行创建或析构就显得荒谬了。  
==========================================
我们不应该假设这样的场合,用户的代码不一定运行在嵌入式系统上。就算是在嵌入式系统中,singleton仍然可以根据需要创建,Scott Meyers的方法也适用,我们只要在应用程序开始的时候调用初始化函数(也就是包含singleton对象的定义的那个函数)即可。

==========================================
而本人没那么多时间去读那些号称为C++大师们写的东西
==========================================
真没时间就算了,如果有时间的话,还是要看一下。我们学数学,看得就是大师们的成果;你学人工智能,书上的内容也是大师的成果;学C++也一样。

 
 
 
#19楼 得分:0回复于:2008-02-20 13:53:44
to 楼上:
==========================================
在大多数场合我要让应用程序一直使用这个Singleton实例
==========================================
我们不应该假设这样的场合。你的assumption不具有通用性。如果用户的程序需要运行时创建singleton或者根本不创建某个singleton对象会怎样?如果用户只需要创建几个不同的singleton对象中的一个或几个(非全部)会怎么样?

 

==========================================
尤其在嵌入式系统中甚至可以将它放入ROM中
==========================================
同样,这仍然不具有通用性。Singleton是可以根据需要创建的,这样会大大限制了singleton的使用场合。

==========================================
因此,在嵌入式系统中,这个实例在机器引导后就存在了,直到复位或关机。所以你说我还要对它在程序运行时进行创建或析构就显得荒谬了。  
==========================================
我们不应该假设这样的场合,用户的代码不一定运行在嵌入式系统上。就算是在嵌入式系统中,singleton仍然可以根据需要创建,Scott Meyers的方法也适用,我们只要在应用程序开始的时候调用初始化函数(也就是包含singleton对象的定义的那个函数)即可。

==========================================
而本人没那么多时间去读那些号称为C++大师们写的东西
==========================================
真没时间就算了,如果有时间的话,还是要看一下。我们学数学,看得就是大师们的成果;你学人工智能,书上的内容也是大师的成果;学C++也一样。

 
 
 
#20楼 得分:0回复于:2008-02-21 18:27:14
To Jim_King_2000 :

 

实际上在你的代码中,由于使用了static,尽管在函数内,但是在加载器加载你的应用程序时仍然会为你的全局唯一的Singleton预留空间。只不过在调用GetInstance()时才调用构造函数(若有的话)。
而我的方法是在加载应用程序后,在初始化代码中调用构造函数(如果有的话),但却带来了额外的安全性。实际上你所谓的存储空间效率两者是一样的,而且结束生命的时间也一样。除非你在构造函数中调用了内存动态分配函数,这样很显然,我的代码将在初始化过程中分配空间;而你的将在第一次调用GetInstance()时分配。

 
 
 
#25楼 得分:0回复于:2008-02-22 13:44:54
我想这里的 Singleton 是针对语言本身提出的解决方案;如果将之与系统相关的设计联系起来,未免有些牵强了。
当然实际应用的时候首先考虑的是系统的设计,然后才是具体的解决方法,而这里的Singleton ,正是在C++中的
解决方法之一。和系统相关的设计应该被分离出去。设计首先要保证 Singleton 的应用是系统安全的,不管是
在何种环境下,这个条件成立以后,才会提到它的具体实现的设计——比如说这里的C++ Singleton 。否则,会把
很多问题缠绕在一起,有时候根本不能解决问题
 
 
 
#29楼 得分:0回复于:2008-11-18 09:27:49
引用 4 楼 Jim_King_2000 的回复:
兄弟,singleton是不用new的。如果用了new,在什么地方去delete它呢?singleton原理的基础是局部static变量。
C/C++ codeclassSingleton 
{private:staticSingleton*instance; 

 

Singleton() 

}public:staticSingleton*GetInstance() 
{staticSingleton singleton;return&singleton

};
这种局部static变量只有在函数被调用的第一次才被创建,随着程序的结束而结束…

private 保证只有一个实现,
static data + static member function !=singleton,
不容易销毁,那本书叫做 不死的凤凰
 
posted @ 2010-08-30 11:32  木子你妹  阅读(913)  评论(0编辑  收藏  举报