Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 单例中静态声明之间的差异_C++_Multithreading_Design Patterns_Singleton - Fatal编程技术网

C++ 单例中静态声明之间的差异

C++ 单例中静态声明之间的差异,c++,multithreading,design-patterns,singleton,C++,Multithreading,Design Patterns,Singleton,(代码摘自罗伯特·尼斯特罗姆的著作) 在上面的书中,作者提出了两种创建singleton类的不同方法: 第一个: class FileSystem { public: static FileSystem& instance() { // Lazy initialize. if (instance_ == NULL) instance_ = new FileSystem(); return *instance_; } private: FileSy

(代码摘自罗伯特·尼斯特罗姆的著作)

在上面的书中,作者提出了两种创建singleton类的不同方法:

第一个:

class FileSystem
{
public:
  static FileSystem& instance()
  {
    // Lazy initialize.
    if (instance_ == NULL) instance_ = new FileSystem();
    return *instance_;
  }

private:
  FileSystem() {}

  static FileSystem* instance_;
};
第二点:

class FileSystem
{
public:
  static FileSystem& instance()
  {
    static FileSystem *instance = new FileSystem();
    return *instance;
  }

private:
  FileSystem() {}
};
后来他指出,第二种方法更适合这样做,因为它是线程安全的,而第一种则不是。
什么使第二个线程安全?

在前一种情况下,如果两个线程试图同时创建实例,可能会创建两个(或更多)单例对象副本。(如果双方都观察到
**实例**
为空,则双方都创建一个
新的
实例)。(更糟糕的是,创建第一个实例的线程可能在后续调用中获得不同的instance值)

而第二个使用
静态
初始化,并且在第一次调用函数时构造对象。因此编译器保证
静态文件系统*instance=newfilesystem()
在程序single的生命周期内最多执行一次,因此atmist对象的单个副本在任何时候都会存在

<>这使得后面的设计线程对于C++ 11和后续的C++编译器来说是安全的。尽管在C++03和C++98实现中,该设计可能不安全


前一种设计的另一个缺点是,对象不能被破坏,而在以后的设计中,可以通过将
实例
的类型更改为
静态文件系统
来破坏对象。i、 e

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


相关:

在第一个代码段中设置
实例
指向单例的指针是一个赋值。它没有得到编译器的任何特殊处理。特别是,如果从并发线程调用
instance()
,则可以多次执行此操作

当两个并发线程尝试计算
实例\==NULL
,并获得
true
时,会产生问题。此时,两个线程都会创建一个新实例,并将其分配给
instance\uu
变量。分配给
实例的第一个指针
泄漏,因为第二个线程立即覆盖它,使对象无法访问


在第二个代码段设置中,
实例
指针是初始化。编译器保证静态变量的初始化最多只进行一次,而不管并发调用
instance()
的线程数量如何。本质上,系统中最多有一个单例的保证是由编译器提供的,没有任何用于处理并发的显式代码。

对于多个线程来说,第一个版本不是线程安全的,可以尝试在没有任何同步的情况下同时读取和修改
实例
,这就导致了比赛状态

第二个版本自C++11以来是线程安全的。引用自(静态局部变量部分):

如果多个线程试图初始化同一个静态本地 同时,初始化只发生一次(类似 使用std::call_once可以获得任意函数的行为

有了这个保证,对
实例的修改只发生一次,并发读取没有问题


尽管如此,第二个版本在C++11之前不是线程安全的。

只有在C++11之后才保证局部静态变量的线程安全一次性初始化。通过定义
文件系统
实例,您就无法实现
延迟初始化
的目标
std::scoped_ptr
在这里可能更合适。@Lingxi You's right C++98部分没有提到多线程,而参考C++98
3.6.3.1[basic.start.term]
我认为构造是未指定的。关于延迟初始化,不要担心,编译器在初始化静态对象时已经很延迟了。你自己试试,真的。对此我没有想清楚。甚至为
文件系统
预先分配的存储也不是问题。不@Lingxi,我的意思没有什么不同。我的观点是,编译器会懒得尽可能晚地延迟实例的创建。(这几乎没有开销,但编译是这样做的)。检查以获得更好的理解。除非程序流遇到所需的声明,否则不会创建静态对象。(我希望声明在技术上是正确的词)我同意你的观点,因为它在实例中不起作用。但是,它不会延迟分配存储,这
instance_uz=newfilesystem()可以。但是,正如我所说的,预分配静态存储并没有多大关系。这只发生在C++11之后。