找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 6130|回复: 4

[转帖]ACE中的设计模式(1)——Singleton

[复制链接]
发表于 2008-3-8 20:52:16 | 显示全部楼层 |阅读模式
ACE中的设计模式(1)——Singleton
Joise.LI @ 2006-9-29,FYT04121
1.         Singleton模式简介
SingletonGOF圣经中最简单的一个模式了,主要用于创建在系统中具有唯一实例又需要到处使用的类,实现起来非常简单。
#include <iostream>
using namespace std;
class my_singleton
{
public:
    static my_singleton *instance();
    void show_my_life();
protected:
    my_singleton();
private:
    static my_singleton *m_instance;
};
my_singleton *my_singleton::m_instance = NULL;
my_singleton *my_singleton::instance()
{
    if (m_instance == NULL)
    {
        m_instance = new my_singleton;
    }
    return m_instance;
}
void my_singleton::show_my_life()
{
cout << "i'm living at " << (void*)this << endl;
}
my_singleton::my_singleton(){}
my_singleton::~my_singleton()
{
    if (m_instance != NULL)
    {
        delete m_instance;
        m_instance = NULL;
    }
}
int main()
{
    my_singleton::instance()->show_my_life();
    return 0;
};
2.         ACE中如何使用Singleton
如上例中,singleton代码非常简单,也很成熟,但是如果在一个系统中有很多地方都需要使用singleton模式,则需要写相当多的类似重复代码,枯燥且低效,如果碰巧使用了ACE,那么使用ACE封装的singleton,则可以更加简单:
class normal_test
{
    friend class ACE_Singleton<normal_test, ACE_Null_Mutex>;
public:
    void show_my_life();
private:
    normal_test();
};
normal_test::normal_test(){}
typedef ACE_Singleton<normal_test, ACE_Null_Mutex> NORMAL_TEST;
void normal_test::show_my_life()
{
    cout << "i'm living at " << (void*)this << endl;
}
int ACE_TMAIN(int argc, ACE_TCHAR *argv[])
{
    my_singleton::instance()->show_my_life();
    NORMAL_TEST::instance()->show_my_life();
    return 0;
};

如上所示,获得了以下优点:
1.    代码减少了一半还多,变得更加简洁、清晰,
2.    使用了double-check技术,免费的获得了线程安全。
3.    没有使用继承、虚函数,性能、效率不会受影响。

3.         ACEsingleton是如何实现的?
以下代码节选自ACE的源代码,为清晰起见,去除了一些无关的宏开关
a)         以下是ACE_Singleton的定义:
template <class TYPE, class ACE_LOCK>
class ACE_Singleton : public ACE_Cleanup
{
public:
  /// Global access point to the Singleton.
  static TYPE *instance (void);
  /// Cleanup method, used by <ace_cleanup_destroyer> to destroy the
  /// ACE_Singleton.
  virtual void cleanup (void *param = 0);
  /// Dump the state of the object.
  static void dump (void);
protected:
  /// Default constructor.
  ACE_Singleton (void);
  /// Contained instance.
  TYPE instance_;
  /// Pointer to the Singleton (ACE_Cleanup) instance.
  static ACE_Singleton<TYPE, ACE_LOCK> *singleton_;
  /// Get pointer to the Singleton instance.
  static ACE_Singleton<TYPE, ACE_LOCK> *&instance_i (void);
};
b)      看一下instance的实现:
template <class TYPE, class ACE_LOCK> TYPE *
ACE_Singleton<TYPE, ACE_LOCK>::instance (void)
{
  ACE_TRACE ("ACE_Singleton<TYPE, ACE_LOCK>::instance");
  ACE_Singleton<TYPE, ACE_LOCK> *&singleton =
    ACE_Singleton<TYPE, ACE_LOCK>::instance_i ();
  // Perform the Double-Check pattern...
  if (singleton == 0)
    {
          static ACE_LOCK *lock = 0;
          if (ACE_Object_Manager::get_singleton_lock (lock) != 0)
            // Failed to acquire the lock!
            return 0;
          ACE_GUARD_RETURN (ACE_LOCK, ace_mon, *lock, 0);
          if (singleton == 0)
            {
              ACE_NEW_RETURN (singleton, (ACE_Singleton<TYPE, ACE_LOCK>), 0);
              // Register for destruction with ACE_Object_Manager.
              ACE_Object_Manager::at_exit (singleton);
            }
    }
  return &singleton->instance_;
}

说明:double-check技术主要是解决线程安全问题,避免在初始化时多线程重入,导致instance被实例化两次。

c)      再看一下instance_i的实现:
template <class TYPE, class ACE_LOCK> ACE_Singleton<TYPE, ACE_LOCK> *&
ACE_Singleton<TYPE, ACE_LOCK>::instance_i (void)
{
  static ACE_Singleton<TYPE, ACE_LOCK> *singleton_ = 0;
  return singleton_;
}

4.         题外话:
ACE_Singleton较好的封装了Singleton模式,对于现代应用系统,一般而言会有相当多的地方需要使用Singleton模式,如线程池、内存池、数据连接池等,通过ACE_Singleton类,可以达到简化代码的目的,同时解决GOF圣经中未提的线程安全的问题。后续我会继续就ACE中的使用的设计模式提出来与大家进行探讨、学习。


原帖地址:[url=http://blog.csdn.net/joise/archive/2006/09/29/1305849.aspx]http://blog.csdn.net/joise/archive/2006/09/29/1305849.aspx[/url]
 楼主| 发表于 2008-3-8 21:01:39 | 显示全部楼层
详细讲下设计模式中的Singleton模式
    SINGLETON,中文翻译为单件模式,是最简单的设计模式之一,采用该模式保证一个类只且仅有一个实例,并提供一个访问它的全局访问点。
    Singleton模式有很多优点:
    1)对唯一实例的受控访问
    2)对类的唯一实例的存取被对象本身所严格控制
    3)减少了系统中所要定义的全局名称。避免使用全局变量来污染名空间
    如何保证一个类仅有一个实例?最好的的办法就是让类自身负责保存它的惟一实例。这个类可以保证没有其它实例可以被创建(通过截取创建新对象请求),并且它可以提供一个访问该实例的方法。简单实现代码如下:
class Singleton
{
public:
    static Singleton *instance();
    someType doOperation();
protected:
    Singleton();
private:
    static Singleton *self;
    someType someData;
};
Singleton *Singleton::self = NULL;
Singleton *Singleton::instance()
{
    if(NULL == self) self = new Singleton;
    return self;
}
    由于Singleton类中保留的self是Singleton*类型的指针,所以程序结束后Singleton::~Singleton()并没有调用,那么何时以及如何来销毁一个Singleton呢?
    可以使用atexit()来注册一个私有的静态函数destroy,该函数在程序退出时候调用,如下:
Singleton *Singleton::instance()
{
    if(NULL == self)
    {
        self = new Singleton;
        ::atexit(destroy);
    }
    return self;
}
void Singleton::destroy()
{
    if(self !=0) delete self;
}
    以上实现基于单线程情况下,由于没有保护机制,在多线程下存在多实例的可能 。于是有以下修正版本:
class Singleton
{
public:
    static Singleton *instance();
    someType doOperation();
protected:
    Singleton();
private:
    static Singleton *self;
    someType someData;
};
Singleton *Singleton::self = NULL;
Singleton *Singleton::instance()
{
    lock(key);
    if(NULL == self)
    {
        self = new Singleton;
        ::atexit(destroy);
    }
    unlock(key);
    return self;
}
    但是这个修正版本在单线程下每次执行过程加解锁的开销是很大的浪费,那么就有了Double-checked Locking Optimization pattern版本:
class Singleton
{
public:
    static Singleton *instance();
    someType doOperation();
protected:
    Singleton();
private:
    static Singleton *self;
    someType someData;
};
Singleton *Singleton::self = NULL;
Singleton *Singleton::instance()
{
    if(NULL == self)
    {
        lock(key);
        if(NULL == self)
        {
            self = new Singleton;
            ::atexit(destroy);
        }
        unlock(key);
    }
    return self;
}

    以上的实现存在Singleton的销毁问题,可以通过另一种实现来解决:
class Singleton
{
public:
    static Singleton *instance();
protected:
    Singleton();
    ~Singleton();
private:
    static Singleton _instance;
};
Singleton Singleton::_instance;
Singleton *Singleton::instance()
{
    return &_instance;
}
    此种方法成功解决了第一种实现Singleton销毁问题,而且使用简单。同时也存在局限性,在第一种实现中,Singleton类实例生成可以延迟到run-time,准确的说,是第一次调用Singleton::instance方法时候才创建;而后一种修正实现在执行main函数前就已经创建。若遇到仅在run-time时刻才能获取足够信息,以创建Singleton实例的情形下,修正方案也不适用。
 楼主| 发表于 2008-3-8 21:04:08 | 显示全部楼层
说说我对Singleton的一些理解:
Singleton从实例的创建时间来分为急式(eager)缓式(lazy)。前者是在初始化时创建,后者是在调用(获得实例)时创建。
急式Singleton,可以用静态实例(类的静态数据成员或成员函数局部静态变量),或者是在类里保存一个静态指针并在定义里给出初值(而非null)。
前者的静态实例的生命期不归你管,特别是类的静态数据成员可能会由于依赖关系出现问题;后者可以控制对象销毁的时间,但是初始化还是不能控制。
关于静态对象的问题,《Imperfect C++》上有讨论,我没有全看懂......
缓式Singleton,(Singleton *Singleton::self = NULL;),可以完全控制生命期,这在ACE中通过“对象生命周期管理者”模式有很好的实现。《ACE 对象生命周期管理者:一种用于控制对象创建和销毁的补充模式》一文里有详细的讲解。
但是,需要注意多线程环境下的初始化问题,对于C++可以应用Double-checked Locking Optimization Pattern来解决,但是这一模式对其他语言不一定可用(比如Java,原因在于self = new Singleton不能保证是原子的,《Java与模式》里有Java实现Singleton的详细论述)。关于Double-checked Locking模式网上有些讨论,我觉得可以归结为一个“读者写者问题”,在C++里使用还是没问题的。《ACE中的Double Checked Locking 模式》一文里有详细的讲解。
ACE里的Singleton是通过C++的template和friend技术实现了一个Singleton Adapter,从而分离了类本身和他的访问模式(singleton)。在实现Singleton时,ACE用的是Lazy + Double-checked Locking + ACE_Object_Manager。
另外,根据Singleton实例的存储位置和生命期的管理方式的不同组合,又出现一些变种的Singleton,所以ACE中有一系列Singleton模板:ACE_Unmanaged_Singleton,ACE_TSS_Singleton,ACE_Unmanaged_TSS_Singleton,ACE_DLL_Singleton_T等。
结合操作系统,Singleton可能是全局范围的、可能是进程范围的、也可能是线程范围的,对于分布式的应用情况更复杂...这些在实际应用中需要考虑。
论述Singleton的一篇好文章:《模式批判之Singleton》
[url=http://blog.csdn.net/wingfiring/archive/2006/08/08/1037079.aspx]http://blog.csdn.net/wingfiring/archive/2006/08/08/1037079.aspx[/url]
和Singleton模式对应的还有Multiton多例模式。
总之,我觉得Singleton要讨论彻底还是很麻烦的。实际使用时,还是要根据需求选择使用什么技术,这就是经验了......
发表于 2008-7-14 15:11:52 | 显示全部楼层

回复 #3 peakzhang 的帖子

我按照你的使用ACE的singleton的例子,然是报错了,我用的是ACE5.5,不知道楼主用的是哪个版本的  
错误信息是error LNK2019: 无法解析的外部符号 "int __cdecl ace_main_i(int,char * * const)" (?ace_main_i@@YAHHQAPAD@Z) ,该符号在函数 "private: virtual int __thiscall ACE_Main::run_i(int,char * * const)" (?run_i@ACE_Main@@EAEHHQAPAD@Z) 中被引用
请教楼主 楼主有联系方式吗,急,十分感谢:)
 楼主| 发表于 2008-7-14 16:41:37 | 显示全部楼层
基础问题。
lib文件没有设置好。
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-5-6 20:23 , Processed in 0.017995 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表