关于Singleton (单件),线程安全

作者:半瓶墨水   链接:http://www.2maomao.com/blog/when-cplusplus-meet-singleton/

很就没写技术相关的blog了,Twitter和Buzz都很少用了,甚至很少有时间静下来思考、敲打键盘

今天面试问到static关键字的时候,有个应试者提到了Singleton的实现,就顺便多问了一点儿,回来自己又查了查

Singleton,单件,四人帮(GoF)写design pattern那本书的时候提出来的,一个进程内有且只有一个实例

说起这些Design Pattern,当时一看,我操,酷毙了,你不知道都不好意思跟别人说你是搞编程的

后来,渐渐的发现,没有银弹,只是有些Design Pattern或许有些淫荡,比如这个Singleton

一个最初的C++标准实现是这样的(S-V1):

//——————Implementation #1 ———————-
//s.h
class Singleton {
  private:
    Singleton();
    static Singleton* instance_;
  public:
    static Singleton* getInstance() {
        if (!instance_)
            instance_ = new Singleton();
        return instance_; } };

//s.cpp
Singleton* Singleton::pInstance_ = 0;

注意其中构造函数是私有的,所有只能通过getInstance构造,构造前又会先检查,所以只有一个

后来有人说,这样不是线程安全的,哦,那就简单点儿,这样吧(S-V2):

//——————Implementation #2 ———————-
//s.h
class Singleton {
  private:
    Singleton();
  public:
    static Singleton* getInstance() {
        static Singleton* instance_ = new Singleton();
        return instance_; } };

结果有人说,这个是依赖于编译器的,GCC能保证static的初始化不会出问题,但是VC就难说了。
于是,有人觉得既然第一个实现稍微改改就行了,只要加一个淫荡的锁(S-V1.1):

//——————Implementation #1.1 ———————-
//s.h
class Singleton {
  private:
    Singleton();
    static Singleton* instance_;
  public:
    static Singleton* getInstance() {
        lock_mutex();
        if (!instance_)
            instance_ = new Singleton();
        unlock_mutex();
        return instance_; } };

//s.cpp
Singleton* Singleton::pInstance_ = 0;

再后来,有人说这样也不行啊,每次get都加锁,效率太低了吧,那就更加淫荡一点,加锁之前我先check,加锁以后,我再check(S-V1.2):

//——————Implementation #1.2 ———————-
//s.h
class Singleton {
  private:
    Singleton();
    static Singleton* instance_;
  public:
    static Singleton* getInstance() {
        if (!instance_) {
          lock_mutex();
          if (!instance_)
              instance_ = new Singleton();
          unlock_mutex();}
        return instance_; } };

//s.cpp
Singleton* Singleton::pInstance_ = 0;

到这里,在搜索网络以前,我曾经认为已经OK了,但是多CPU多线程的复杂超乎想象,有人说,后一个实现不是线程安全的,因为对于instance的读和写并不是原子操作会发生写一半读一半的情况!
好吧,对效率没啥要求每个singleton就只会用个几次的用S-V1.1,没有多线程的用S-V2


========== 以下我也不是很懂的分割线 ==========

难道真的没办法优化S-V1.1了吗?

有办法,那个instance读一半写一半的情况对于int这个类型是不会发生的,那么,就多来一步,check一个int类型的标识吧:

//——————Implementation #1.3 ———————-
//s.h
class Singleton {
  private:
    Singleton();
    static Singleton* instance_;
    static int flag_;
  public:
    static Singleton* getInstance() {
        if (!flag_) {
          lock_mutex();
          if (!instance_)
              instance_ = new Singleton();
          unlock_mutex();}
          flag_ = 1;
        return instance_; } };

嘿嘿,这下你该满足了吧,NO!,还是有问题的,具体啥问题,参见这里那里,代码已经贴太多了。


进行到这里,你应该已经体会到,Singleton是很变态的,Design Pattern是很淫荡的。。。

Singleton最淫荡的地方在于:真正需要它的机会太少太少了,更多讨论参见:
[1] Singleton,你坏。。。在哪里?
[2] Singleton,银弹还是狗屎
[3] Singleton,何日才能用到你


路人甲:OMG,这是什么代码格式啊!
好吧,肯定有人不喜欢这个缩进,但是我敢肯定,肯定有人知道我喜欢Python了 😀

7 条评论 发表在“关于Singleton (单件),线程安全”上

  1. Googol Lee说道:

    可以用pthread提供的once来创建。不过,不只是创建,单件在多线程下想顺利销毁简直就是不可能的。

  2. Xw.Y说道:

    我是来顶代码格式的,什么Singleton啊DesignPattern啊最讨厌了。

  3. Xw.Y说道:

    😡 居然从 ff 上抓到我不同的马甲,这个博客太邪恶了

  4. NetCobra说道:

    我说这代码看着很别扭,明明是C++的,怎么看怎么不顺眼,看到最后才想起来博主是Python的……

  5. withoutmind说道:

    呵呵,不错啊,还有兴趣写这些…

  6. 半瓶墨水说道:

    @withoutmind 小明明也来逛啊
    最近风声鹤唳人心惶惶,谈技术的太少了

  7. 甄码农说道:

    单例是淫荡的,这似乎因为计算机多核发展的太快了。

留下回复