Meyers如何实现Singleton实际上是一个Singleton [英] How is Meyers' implementation of a Singleton actually a Singleton

查看:260
本文介绍了Meyers如何实现Singleton实际上是一个Singleton的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了很多关于Singletons,当他们应该和不应该使用,以及如何安全地实施它们。我在C ++ 11中编写,并且遇到了Meyer的一个单例的惰性初始化实现,如这个问题。



这个实现是:

  static Singleton& instance()
{
static Singleton s;
return s;
}



我理解这是线程如何从其他问题在这里SO,我不明白是怎么这实际上是一个单例模式。我已在其他语言中实现单例,并且这些常见的例子来自
维基百科

  public class SingletonDemo {
private static volatile SingletonDemo instance = null;

private SingletonDemo(){}

public static SingletonDemo getInstance(){
if(instance == null){
synchronized(SingletonDemo .class ){
if(instance == null){
instance = new SingletonDemo();
}
}
}
return instance;
}
}

当我看第二个例子时,直觉这是一个单例,因为类保存对自身的一个实例的引用,并且只有返回该实例。但是,在第一个例子中,我不明白这是如何防止存在的对象的两个实例。所以我的问题是:


  1. 第一个实现如何强制实施单例模式?我假设它与static关键字有关,但我希望有人可以深入解释发生的事情。

  2. 在这两种实现风格之间,是一个优选在另一个?


  3. 感谢任何帮助,

    解决方案

    这是一个单例,因为 static 函数local的存储持续时间意味着程序中只存在该局部的一个实例。



    在这种情况下,这可以被认为等同于下面的C ++ 98(甚至可能由编译器模糊地实现):

      static bool __guard = false; 
    static char __storage [sizeof(Singleton)]; //也对齐它

    Singleton& Instance(){
    if(!__ guard){
    __guard = true;
    new(__storage)Singleton();
    }
    return * reinterpret_cast< Singleton *>(__ storage);
    }

    //当进程退出时自动调用
    void __destruct(){
    if(__guard)
    reinterpret_cast< Singleton *> ) - >〜Singleton();
    }

    线程安全位使它变得更复杂,同样的事情。



    看一下C ++ 11的实际实现,每个静态都有一个 guard变量 ,其也用于屏障和螺纹。看看Clang的AMD64输出:

      Singleton& instance(){
    static Singleton实例;
    return instance;
    }

    实例的AMD64程序集从Ubuntu的Clang 3.0 on AMD64 at -O1(由 http://gcc.godbolt.org/ 提供)是:

      instance():#@instance()
    pushq%rbp
    movq%rsp,%rbp $ b b movb guard实例的变量():: instance(%rip),%al
    testb%al,%al
    jne .LBB0_3
    movl guard变量, %edi
    callq __cxa_guard_acquire
    testl%eax,%eax
    je .LBB0_3
    movl instance():: instance,%edi
    callq Singleton :: Singleton
    movl保护变量的实例()::实例,%edi
    callq __cxa_guard_release
    .LBB0_3:
    movl instance():: instance,%eax
    popq% rbp
    ret

    您可以看到它引用一个全局保护来查看是否需要初始化,使用 __ cxa_guard_acquire ,再次测试初始化​​,等等。几乎每一种方式,像从维基百科发布的版本,除了使用AMD64程序集和在中指定的符号/布局Itanium ABI



    请注意,如果您运行该测试,您应该给 Singleton 构造函数,所以它不是一个POD,否则优化器会意识到没有任何意义,做所有的保护/锁定工作。


    I have been reading a lot about Singletons, when they should and shouldn't be used, and how to implement them safely. I am writing in C++11, and have come across the Meyer's lazy initialized implementation of a singleton, as seen in this question.

    This implementation is:

    static Singleton& instance()
    {
         static Singleton s;
         return s;
    }
    

    I understand how this is thread safe from other questions here on SO, but what I don't understand is how this is actually a singleton pattern. I have implemented singletons in other languages, and these always end up something like this example from Wikipedia:

    public class SingletonDemo {
            private static volatile SingletonDemo instance = null;
    
            private SingletonDemo() {       }
    
            public static SingletonDemo getInstance() {
                    if (instance == null) {
                            synchronized (SingletonDemo .class){
                                    if (instance == null) {
                                            instance = new SingletonDemo ();
                                    }
                          }
                    }
                    return instance;
            }
    }
    

    When I look at this second example, it is very intuitive how this is a singleton, since the class holds a reference to one instance of itself, and only ever returns that instance. However, in the first example, I don't understand how this prevents there ever existing two instances of the object. So my questions are:

    1. How does the first implementation enforce a singleton pattern? I assume it has to do with the static keyword, but I am hoping that someone can explain to me in depth what is happening under the hood.
    2. Between these two implementation styles, is one preferable over the other? What are the pros and cons?

    Thanks for any help,

    解决方案

    This is a singleton because static storage duration for a function local means that only one instance of that local exists in the program.

    Under the hood, this can very roughly be considered to be equivalent to the following C++98 (and might even be implemented vaguely like this by a compiler):

    static bool __guard = false;
    static char __storage[sizeof(Singleton)]; // also align it
    
    Singleton& Instance() {
      if (!__guard ) {
        __guard = true;
        new (__storage) Singleton();
      }
      return *reinterpret_cast<Singleton*>(__storage);
    }
    
    // called automatically when the process exits
    void __destruct() {
      if (__guard)
        reinterpret_cast<Singleton*>(__storage)->~Singleton();
    }
    

    The thread safety bits make it get a bit more complicated, but it's essentially the same thing.

    Looking at an actual implementation for C++11, there is a guard variable for each static (like the boolean above), which is also used for barriers and threads. Look at Clang's AMD64 output for:

    Singleton& instance() {
       static Singleton instance;
       return instance;
    }
    

    The AMD64 assembly for instance from Ubuntu's Clang 3.0 on AMD64 at -O1 (courtesy of http://gcc.godbolt.org/ is:

    instance():                           # @instance()
      pushq %rbp
      movq  %rsp, %rbp
      movb  guard variable for instance()::instance(%rip), %al
      testb %al, %al
      jne   .LBB0_3
      movl  guard variable for instance()::instance, %edi
      callq __cxa_guard_acquire
      testl %eax, %eax
      je    .LBB0_3
      movl  instance()::instance, %edi
      callq Singleton::Singleton()
      movl  guard variable for instance()::instance, %edi
      callq __cxa_guard_release
    .LBB0_3:
      movl  instance()::instance, %eax
      popq  %rbp
      ret
    

    You can see that it references a global guard to see if initialization is required, uses __cxa_guard_acquire, tests the initialization again, and so on. Exactly in almost every way like version you posted from Wikipedia, except using AMD64 assembly and the symbols/layout specified in the Itanium ABI.

    Note that if you run that test you should give Singleton a non-trivial constructor so it's not a POD, otherwise the optimizer will realize that there's no point to doing all that guard/locking work.

    这篇关于Meyers如何实现Singleton实际上是一个Singleton的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆