Meyers的单例实现是如何实现的呢

时间:2022-01-16 12:00:05

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.

我已经读了很多关于单身人士的文章,什么时候应该使用,什么时候不应该使用,以及如何安全地实现它们。我正在用c++ 11编写,并遇到了Meyer的单例对象的惰性初始化实现,如图所示。

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. 第一个实现如何执行单例模式?我认为这与静态关键字有关,但我希望有人能深入地向我解释在引擎盖下面发生了什么。
  3. Between these two implementation styles, is one preferable over the other? What are the pros and cons?
  4. 在这两种实现风格之间,哪种更可取?赞成和反对的理由是什么?

Thanks for any help,

感谢任何帮助,

2 个解决方案

#1


40  

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):

在这个框架下,可以粗略地认为这相当于下面的c++ 98(甚至可能被编译器模糊地实现):

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:

查看c++ 11的实际实现,每个静态都有一个保护变量(如上面的布尔值),它也用于屏障和线程。查看Clang的AMD64输出:

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:

AMD64在AMD64上的AMD64(由http://gcc.godbolt.org/提供)在AMD64上的AMD64汇编。

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.

您可以看到,它引用了一个全局保护,以查看是否需要初始化、使用__cxa_guard_acquire、再次测试初始化,等等。除了使用AMD64汇编和在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.

注意,如果您运行该测试,您应该给Singleton一个非平凡的构造函数,使其不是POD,否则优化器将会意识到没有必要做所有的保护/锁定工作。

#2


13  

// Singleton.hpp
class Singleton {
public:
    static Singleton& Instance() {
        static Singleton S;
        return S;
    }

private:
    Singleton();
    ~Singleton();
};

This implementation is known as Meyers' Singleton. Scott Meyers says:

这个实现被称为Meyers的Singleton。Scott Meyers说:

"This approach is founded on C++'s guarantee that local static objects are initialized when the object's definition is first encountered during a call to that function." ... "As a bonus, if you never call a function emulating a non-local static object, you never incur the cost of constructing and destructing the object."

“这种方法基于c++的保证,即在调用该函数时首次遇到对象的定义时初始化本地静态对象。”…“作为奖励,如果您从不调用模拟非本地静态对象的函数,您就不会产生构建和销毁对象的成本。”

When you call Singleton& s=Singleton::Instance() first time the object is created and every next call to Singleton::Instance() results with the same object being returned. Main issue:

当您调用singleton&s =Singleton::实例()第一次创建对象,每次调用Singleton::实例()结果与返回的对象相同。主要问题:

  • subject to Destruction Order Fiasco (the equivalent to the Initialization Order Fiasco)
  • 遭受破坏顺序的失败(等同于初始化顺序的失败)

Another implementation is called the trusty leaky Singleton.

另一个实现称为可靠的leaky Singleton。

class Singleton {
public:
    static Singleton& Instance() {
        if (I == nullptr) { I = new Singleton(); }
        return *I;
    }

private:
    Singleton();
    ~Singleton();

    static Singleton* I;
};

// Singleton.cpp
Singleton* Singleton::I = 0;

Two issues:

两个问题:

  • leaks, unless you implement a Release and make sure to call it (once)
  • 泄漏,除非您实现了一个发布并确保调用它(一次)
  • not thread safe
  • 不是线程安全的

#1


40  

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):

在这个框架下,可以粗略地认为这相当于下面的c++ 98(甚至可能被编译器模糊地实现):

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:

查看c++ 11的实际实现,每个静态都有一个保护变量(如上面的布尔值),它也用于屏障和线程。查看Clang的AMD64输出:

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:

AMD64在AMD64上的AMD64(由http://gcc.godbolt.org/提供)在AMD64上的AMD64汇编。

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.

您可以看到,它引用了一个全局保护,以查看是否需要初始化、使用__cxa_guard_acquire、再次测试初始化,等等。除了使用AMD64汇编和在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.

注意,如果您运行该测试,您应该给Singleton一个非平凡的构造函数,使其不是POD,否则优化器将会意识到没有必要做所有的保护/锁定工作。

#2


13  

// Singleton.hpp
class Singleton {
public:
    static Singleton& Instance() {
        static Singleton S;
        return S;
    }

private:
    Singleton();
    ~Singleton();
};

This implementation is known as Meyers' Singleton. Scott Meyers says:

这个实现被称为Meyers的Singleton。Scott Meyers说:

"This approach is founded on C++'s guarantee that local static objects are initialized when the object's definition is first encountered during a call to that function." ... "As a bonus, if you never call a function emulating a non-local static object, you never incur the cost of constructing and destructing the object."

“这种方法基于c++的保证,即在调用该函数时首次遇到对象的定义时初始化本地静态对象。”…“作为奖励,如果您从不调用模拟非本地静态对象的函数,您就不会产生构建和销毁对象的成本。”

When you call Singleton& s=Singleton::Instance() first time the object is created and every next call to Singleton::Instance() results with the same object being returned. Main issue:

当您调用singleton&s =Singleton::实例()第一次创建对象,每次调用Singleton::实例()结果与返回的对象相同。主要问题:

  • subject to Destruction Order Fiasco (the equivalent to the Initialization Order Fiasco)
  • 遭受破坏顺序的失败(等同于初始化顺序的失败)

Another implementation is called the trusty leaky Singleton.

另一个实现称为可靠的leaky Singleton。

class Singleton {
public:
    static Singleton& Instance() {
        if (I == nullptr) { I = new Singleton(); }
        return *I;
    }

private:
    Singleton();
    ~Singleton();

    static Singleton* I;
};

// Singleton.cpp
Singleton* Singleton::I = 0;

Two issues:

两个问题:

  • leaks, unless you implement a Release and make sure to call it (once)
  • 泄漏,除非您实现了一个发布并确保调用它(一次)
  • not thread safe
  • 不是线程安全的