足球游戏_中国足彩网¥体育资讯$

asp.net中c++单例实现问题分析
来源:易贤网 阅读:673 次 日期:2014-10-21 09:53:21
温馨提示:易贤网小编为您整理了“asp.net中c++单例实现问题分析”,方便广大网友查阅!

方案一

代码如下:

class qmmanager

{

public:

static qmmanager &instance()

{

static qmmanager instance_;

return instance_;

}

}

这是最简单的版本,在单线程下(或者是c++0x下)是没任何问题的,但在多线程下就不行了,因为static qmmanager instance_;这句话不是线程安全的。

在局部作用域下的静态变量在编译时,编译器会创建一个附加变量标识静态变量是否被初始化,会被编译器变成像下面这样(伪代码):

代码如下:

static qmmanager &instance()

{

static bool constructed = false;

static uninitialized qmmanager instance_;

if (!constructed) {

constructed = true;

new(&s) qmmanager; //construct it

}

return instance_;

}

这里有竞争条件,两个线程同时调用instance()时,一个线程运行到if语句进入后还没设constructed值,此时切换到另一线程,constructed值还是false,同样进入到if语句里初始化变量,两个线程都执行了这个单例类的初始化,就不再是单例了。

方案二

一个解决方法是加锁:

代码如下:

static qmmanager &instance()

{

lock(); //锁自己实现

static qmmanager instance_;

unlock();

return instance_;

}

但这样每次调用instance()都要加锁解锁,代价略大。

方案三

那再改变一下,把内部静态实例变成类的静态成员,在外部初始化,也就是在include了文件,main函数执行前就初始化这个实例,就不会有线程重入问题了:

代码如下:

class qmmanager

{

protected:

static qmmanager instance_;

qmmanager();

~qmmanager(){};

public:

static qmmanager *instance()

{

return &instance_;

}

void do_something();

};

qmmanager qmmanager::instance_; //外部初始化

这被称为饿汉模式,程序一加载就初始化,不管有没有调用到。

看似没问题,但还是有坑,在一个2b情况下会有问题:在这个单例类的构造函数里调用另一个单例类的方法可能会有问题。

看例子:

代码如下:

//.h

class qmmanager

{

protected:

static qmmanager instance_;

qmmanager();

~qmmanager(){};

public:

static qmmanager *instance()

{

return &instance_;

}

};

class qmsqlite

{

protected:

static qmsqlite instance_;

qmsqlite();

~qmsqlite(){};

public:

static qmsqlite *instance()

{

return &instance_;

}

void do_something();

};

qmmanager qmmanager::instance_;

qmsqlite qmsqlite::instance_;

//.cpp

qmmanager::qmmanager()

{

printf(qmmanager constructorn);

qmsqlite::instance()->do_something();

}

qmsqlite::qmsqlite()

{

printf(qmsqlite constructorn);

}

void qmsqlite::do_something()

{

printf(qmsqlite do_somethingn);

}

这里qmmanager的构造函数调用了qmsqlite的instance函数,但此时qmsqlite::instance_可能还没有初始化。

这里的执行流程:程序开始后,在执行main前,执行到qmmanager qmmanager::instance_;这句代码,初始化qmmanager里的instance_静态变量,调用到qmmanager的构造函数,在构造函数里调用qmsqlite::instance(),取qmsqlite里的instance_静态变量,但此时qmsqlite::instance_还没初始化,问题就出现了。

那这里会crash吗,测试结果是不会,这应该跟编译器有关,静态数据区空间应该是先被分配了,在调用qmmanager构造函数前,qmsqlite成员函数在内存里已经存在了,只是还未调到它的构造函数,所以输出是这样:

qmmanager constructor

qmsqlite do_something

qmsqlite constructor

方案四

那这个问题怎么解决呢,单例对象作为静态局部变量有线程安全问题,作为类静态全局变量在一开始初始化,有以上2b问题,那结合下上述两种方式,可以解决这两个问题。boost的实现方式是:单例对象作为静态局部变量,但增加一个辅助类让单例对象可以在一开始就初始化。如下:

代码如下:

//.h

class qmmanager

{

protected:

struct object_creator

{

object_creator()

{

qmmanager::instance();

}

inline void do_nothing() const {}

};

static object_creator create_object_;

qmmanager();

~qmmanager(){};

public:

static qmmanager *instance()

{

static qmmanager instance;

return &instance;

}

};

qmmanager::object_creator qmmanager::create_object_;

class qmsqlite

{

protected:

qmsqlite();

~qmsqlite(){};

struct object_creator

{

object_creator()

{

qmsqlite::instance();

}

inline void do_nothing() const {}

};

static object_creator create_object_;

public:

static qmsqlite *instance()

{

static qmsqlite instance;

return &instance;

}

void do_something();

};

qmmanager::object_creator qmmanager::create_object_;

qmsqlite::object_creator qmsqlite::create_object_;

结合方案3的.cpp,这下可以看到正确的输出和调用了:

qmmanager constructor

qmsqlite constructor

qmsqlite do_something

来看看这里的执行流程:

初始化qmmanager类全局静态变量create_object_

->调用object_creator的构造函数

->调用qmmanager::instance()方法初始化单例

->执行qmmanager的构造函数

->调用qmsqlite::instance()

->初始化局部静态变量qmsqlite instance

->执行qmsqlite的构造函数,然后返回这个单例。

跟方案三的区别在于qmmanager调用qmsqlite单例时,方案3是取到全局静态变量,此时这个变量未初始化,而方案四的单例是静态局部变量,此时调用会初始化。

跟最初方案一的区别是在main函数前就初始化了单例,不会有线程安全问题。

最终boost

上面为了说明清楚点去除了模版,实际使用是用模版,不用写那么多重复代码,这是boost库的模板实现:

代码如下:

template <typename t>

struct singleton

{

struct object_creator

{

object_creator(){ singleton<t>::instance(); }

inline void do_nothing()const {}

};

static object_creator create_object;

public:

typedef t object_type;

static object_type& instance()

{

static object_type obj;

//据说这个do_nothing是确保create_object构造函数被调用

//这跟模板的编译有关

create_object.do_nothing();

return obj;

}

};

template <typename t> typename singleton<t>::object_creator singleton<t>::create_object;

class qmmanager

{

protected:

qmmanager();

~qmmanager(){};

friend class singleton<qmmanager>;

public:

void do_something(){};

};

int main()

{

singleton<qmmanager>::instance()->do_something();

return 0;

}

其实boost库这样的实现像打了几个补丁,用了一些奇技淫巧,虽然确实绕过了坑实现了需求,但感觉挺不好的。

中国足彩网信息请查看IT技术专栏

中国足彩网信息请查看网络编程
易贤网手机网站地址:asp.net中c++单例实现问题分析
由于各方面情况的不断调整与变化,易贤网提供的所有考试信息和咨询回复仅供参考,敬请考生以权威部门公布的正式信息和咨询为准!

2025国考·省考课程试听报名

  • 报班类型
  • 姓名
  • 手机号
  • 验证码
关于我们 | 联系我们 | 人才招聘 | 网站声明 | 网站帮助 | 非正式的简要咨询 | 简要咨询须知 | 加入群交流 | 手机站点 | 投诉建议
工业和信息化部备案号:滇ICP备2023014141号-1 足球游戏_中国足彩网¥体育资讯$ 滇公网安备53010202001879号 人力资源服务许可证:(云)人服证字(2023)第0102001523号
云南网警备案专用图标
联系电话:0871-65099533/13759567129 获取招聘考试信息及咨询关注公众号:hfpxwx
咨询QQ:526150442(9:00—18:00)版权所有:易贤网
云南网警报警专用图标