<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lost HIT - Zealot&#039;s Blog &#187; pattern</title>
	<atom:link href="http://blog.losthit.com/archives/tag/pattern/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.losthit.com</link>
	<description>关注 linux/c/c++/python/web开发,互联网数据抓取与挖掘</description>
	<lastBuildDate>Tue, 31 Jan 2012 09:27:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>单件模式：Singleton那些事</title>
		<link>http://blog.losthit.com/archives/singleton-everthing/</link>
		<comments>http://blog.losthit.com/archives/singleton-everthing/#comments</comments>
		<pubDate>Sun, 28 Feb 2010 22:56:01 +0000</pubDate>
		<dc:creator>Zealot</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[设计模式]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[DCLP]]></category>
		<category><![CDATA[gtest]]></category>
		<category><![CDATA[pattern]]></category>
		<category><![CDATA[singleton]]></category>

		<guid isPermaLink="false">http://blog.losthit.com/?p=287</guid>
		<description><![CDATA[单件模式确保一个类只有一个实例，并提供一个全局的访问点。 &#8211;《Head First 设计模式》 有些时候，我们恰好需要这样一种设计，保证我们实例化的类在进程地址空间内只有一个实例，有了... ]]></description>
			<content:encoded><![CDATA[<blockquote><p><strong>单件模式</strong>确保一个类只有一个实例，并提供一个全局的访问点。      &#8211;《Head First 设计模式》</p></blockquote>
<p>有些时候，我们恰好需要这样一种设计，保证我们实例化的类在进程地址空间内只有一个实例，有了Singleton，一切似乎都简单明了，其实暗藏杀机。</p>
<p><strong>1. C++实现Singleton模式简单示例</strong></p>
<p>简单的C++实现示例代码</p>
<pre name="code" class="c++">
class Singleton {
    public:
        static Singleton* GetInstance(void) {
            if (Singleton::m_instance == NULL) {
                Singleton::m_instance = new Singleton();
            }
            return Singleton::m_instance;
        }
    private:
        static Singleton* m_instance;
        Singleton() {}
};
</pre>
<p>使用gtest测试，简单地测试一下每次调用GetInstance是否返回同一地址</p>
<pre name="code" class="c++">
#include <cstdlib>
#include <iostream>

#include <gtest/gtest.h>
using namespace std;
#define NEW_COUNT 4096

class Singleton {
    public:
        static Singleton* GetInstance(void) {
            if (Singleton::m_instance == NULL) {
                Singleton::m_instance = new Singleton();
            }
            return Singleton::m_instance;
        }
    private:
        static Singleton* m_instance;
        Singleton() {}
};

Singleton* Singleton::m_instance = NULL;

TEST(Singleton, sample) {
    Singleton* init_instance = Singleton::GetInstance();
    ASSERT_NE(init_instance, (Singleton*)NULL);

    Singleton* p = NULL;
    ASSERT_EQ(p, (Singleton*)NULL);

    for (int i = 0; i < NEW_COUNT; ++i) {
        p = Singleton::GetInstance();
        ASSERT_EQ(init_instance, p);
    }
}

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&#038;argc, argv);
    return RUN_ALL_TESTS();
}
</pre>
<p>测试结果显示没有问题：<br />
<code>$ ./singleton<br />
[==========] Running 1 test from 1 test case.<br />
[----------] Global test environment set-up.<br />
[----------] 1 test from Singleton<br />
[ RUN      ] Singleton.sample<br />
[       OK ] Singleton.sample (1 ms)<br />
[----------] 1 test from Singleton (1 ms total)</code></p>
<p><code> </code></p>
<p><code>[----------] Global test environment tear-down<br />
[==========] 1 test from 1 test case ran. (2 ms total)<br />
[  PASSED  ] 1 test.</code></p>
<p>当然，这种测试没有太大意义。给出测试结果，主要用于之后的对比。</p>
<p><strong>2. python程序中调用Singleton模式的C++代码</strong><br />
初次接触Singleton后发现，使用Singleton模式的C++代码融合到python中有意想不到的惊喜：如果有某个类初始化需要很长时间，而之后调用的速度都很快的话，使用Singleton模式，然后在python中多次调用时，不会出现重复的初始化操作。<br />
简单地尝试了一下</p>
<p>首先在singleton.cpp中添加了一行输出，标识一次初始化操作：</p>
<pre name="code" class="c++">
Singleton* Singleton::GetInstance(void) {
    if (Singleton::m_instance == NULL) {
        Singleton::m_instance = new Singleton();
        std::cout << "initialize here." << std::endl;
    }
    return Singleton::m_instance;
}
</pre>
<p>封装的代码如下<br />
<code>$ cat _pysingleton.cpp -n</code></p>
<pre name="code" class="c++">
#include <Python.h>

#include "singleton.h"

using namespace std;

PyObject * GetInstance(PyObject *self, PyObject *args) {
    Singleton::GetInstance();
    return Py_BuildValue("s", NULL);
}

static PyMethodDef _pysingletonMethods[] = {
    {"GetInstance", GetInstance, METH_VARARGS, "get instance."},
    {NULL, NULL},
};

PyMODINIT_FUNC init_pysingleton(void) {
    PyObject * m;
    m = Py_InitModule("_pysingleton", _pysingletonMethods);
}
</pre>
<p>编译出来共享库_pysingleton.so<br />
<code>$ cat Makefile  -n </code></p>
<pre name="code">
CC                =       g++
FLAGS           =       -ggdb -O2 -Wall
INC               =       -I/usr/include/python2.5
LIB               =       -lgtest -lpython2.5
LLIB              =

%.o:%.cpp
        $(CC) $(FLAGS) $(INC) -c $<

_pysingleton.so: singleton.o _pysingleton.o
        $(CC) -shared $(CFLAGS) $(LLIB) $(INCLUDE) $^ -o $@

clean:
        rm -f *.so
        rm -f *.o
        rm -f *.pyc
        rm -f singleton
</pre>
<p>写个简单的python脚本测试一下在单一进程空间里，是否真的只调用一次<br />
<code>$ cat pysingleton.py -n</code></p>
<pre name="code" class="python">
#!/usr/bin/env python
# -*- coding: utf-8 -*-

''' sample for pysingleton
'''

import os
import sys
import _pysingleton

class PySingleton:
    def __init__(self):
        _pysingleton.GetInstance()

def main():
    ''' main function
    '''
    for i in xrange(3):
        PySingleton()
    print 'Done'

if __name__ == '__main__':
    main()
</pre>
<p>结果是<br />
<code> $ ./pysingleton.py<br />
initialize here.<br />
Done </code></p>
<p><strong>3. 多线程中使用Singleton模式</strong><br />
多线程里就会遇到一个double-check问题 (double-check-lock problem, DCLP)<br />
首先，静态变量m_instance作用域是全局的，处于临界区，在多线程需要加锁。这一点很好理解。<br />
但是，简单的在if外面加锁势必会影响性能，毕竟只有第一次调用才会生效，每次都有一次锁操作太费时，也没必要。<br />
于是，就会有double-check方法了，伪代码为<br />
<code>if (Singleton::m_instance == NULL)  // first check<br />
lock<br />
if (Singleton::m_instance == NULL)  // second check </code><br />
从这里可以看出，double-check有效地避免了重复锁操作问题。<br />
然而，问题还没有结束。采用double-check的话，又会引入另一个问题。在这里<br />
<code>Singleton::m_instance = new Singleton(); </code><br />
编译器可能会做个优化，首先将一个有效的内存地址返回给m_instance，然后再调用构造函数初始化。这样很有下一次double-check中首次检查通过，但是得到的内存地址指向的是一个未经初始化的类。后果怎么样，只有天知道了。<br />
更多细节，参考这里：<a href="http://www.ibm.com/developerworks/library/j-dcl.html" target="_blank">http://www.ibm.com/developerworks/library/j-dcl.html</a></p>
<p>a. 最简单的修改方法是<br />
<code>Singleton* temp = new Singleton();<br />
m_instance = temp; </code><br />
但是一个自作聪明地编译器还是会把结果优化成难以想象的样子<br />
b. 另一个方法就是使用volatile关键字，用以声明不受编译器优化的影响<br />
比如 <code>Singleton* volatile temp = new Singleton();</code><br />
但是远远不够，temp是volatile，但是*temp呢，temp-&gt;foo,temp-&gt;bar...这些成员呢？所以，要改的话，得把全世界都改成volatile，除了那个lock-_-|||</p>
<p><strong>4. 以上都只考虑单核，再考虑一下多核机器，问题将会更加复杂</strong></p>
<blockquote><p><strong>总结：珍爱生命，远离Singleton。</strong>尤其是不要把Singleton放进通用库中，还有C++中那个叫template的高级玩意儿，都搞在一起老天都很难保证会发生些什么。</p></blockquote>
<p>附<br />
1. <strong>这里也在讨论单例模式</strong>：<a href="http://code.google.com/p/google-singleton-detector/wiki/WhySingletonsAreControversial" target="_blank">Why Singletons Are Controversial</a><br />
2. 代码托管到<a href="code.google.com" target="_blank">Google Code</a>，欢迎围观、讨论、批评、挑bug：<a href="http://code.google.com/p/zldemo/source/browse/#svn/trunk/blog/singleton_everything" target="_blank">点此进入</a>（http://code.google.com/p/zldemo/source/browse/#svn/trunk/blog/singleton_everything）<br />
(墙内用户请不要使用https访问)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.losthit.com/archives/singleton-everthing/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

