C++ c++;STL线程安全的单例实现

C++ c++;STL线程安全的单例实现,c++,multithreading,c++11,stl,singleton,C++,Multithreading,C++11,Stl,Singleton,我一直在尝试用STL中的一些C++11特性实现一个单例。我阅读了一些实现,发现这非常好:我做了一些更改,并在VS2013上使用了下面的代码,但我仍然想知道: a) 这个实现是线程安全的吗 b) 从GetInstance而不是引用返回共享_ptr可以吗(良好做法) 注:我的singleton是一个OpenGL接口(这就是名字的原因) HandlerOpenGL.h #pragma once // STL headers #include <memory> // shared_pt

我一直在尝试用STL中的一些C++11特性实现一个单例。我阅读了一些实现,发现这非常好:我做了一些更改,并在VS2013上使用了下面的代码,但我仍然想知道:

a) 这个实现是线程安全的吗

b) 从GetInstance而不是引用返回共享_ptr可以吗(良好做法)

注:我的singleton是一个OpenGL接口(这就是名字的原因)

HandlerOpenGL.h

#pragma once



// STL headers
#include <memory> // shared_ptr
#include <mutex> // once_flag



// OpenGL Handler Singleton
class HandlerOpenGL
{
public:
    // Constructor/Destructor interface
    static std::shared_ptr<HandlerOpenGL> GetInstance(void);    // Only way to access singleton
    ~HandlerOpenGL(void);
    HandlerOpenGL(const HandlerOpenGL&) = delete;   // Avoid any copy/move
    HandlerOpenGL(HandlerOpenGL&&) = delete;
    HandlerOpenGL& operator=(const HandlerOpenGL&) = delete;
    HandlerOpenGL& operator=(HandlerOpenGL&&) = delete;

private:
    // Hide construction method/variables
    HandlerOpenGL(void);    // Private to be created once
    static std::shared_ptr<HandlerOpenGL> _instance;
    static std::once_flag _flag;
};
#pragma一次
//STL头文件
#包括//shared\u ptr
#包括//once\u标志
//OpenGL处理程序单例
类句柄OpenGL
{
公众:
//构造函数/析构函数接口
静态std::shared_ptr GetInstance(void);//访问singleton的唯一方法
~HandlerOpenGL(无效);
HandlerOpenGL(const HandlerOpenGL&)=删除;//避免任何复制/移动
HandlerOpenGL(HandlerOpenGL&&)=删除;
HandlerOpenGL&运算符=(常量HandlerOpenGL&)=删除;
HandlerOpenGL&operator=(HandlerOpenGL&&)=删除;
私人:
//隐藏构造方法/变量
HandlerOpenGL(void);//要创建一次的私有
静态std::共享的\u ptr\u实例;
静态标准::一次_标志_标志;
};
HandlerOpenGL.cpp

// Own header
#include "HandlerOpenGL.h"
// STL headers
#include <memory> // shared_ptr, make_shared
#include <mutex> // once_flag, call_once



// instanciate static members
std::shared_ptr<HandlerOpenGL> HandlerOpenGL::_instance(nullptr);
std::once_flag HandlerOpenGL::_flag;



std::shared_ptr<HandlerOpenGL> HandlerOpenGL::GetInstance(void)
{
    std::call_once(_flag, [](){ _instance.reset(new HandlerOpenGL); });
    return _instance;
}



HandlerOpenGL::~HandlerOpenGL(void)
{
}



HandlerOpenGL::HandlerOpenGL(void)
{
}
//自己的头
#包括“HandlerOpenGL.h”
//STL头文件
#包括//shared\u ptr、make\u shared
#包括//once\u标志,调用\u once
//实例化静态成员
std::shared_ptr handleOpenGL::_实例(nullptr);
std::once_标志句柄OpenGL::_标志;
std::shared_ptr HandlerOpenGL::GetInstance(无效)
{
std::调用_一次(_标志,[](){_instance.reset(newhandleopengl);});
返回_实例;
}
HandlerOpenGL::~HandlerOpenGL(无效)
{
}
HandlerOpenGL::HandlerOpenGL(无效)
{
}

我看不出在那里使用
共享\u ptr
有什么意义。如果是单例,则不会复制它。那么为什么要使用
共享\u ptr

我还相信,Meyers单例操作简单得多,需要更少的输入,而且不依赖于动态分配,所以我想知道为什么有人会做其他事情


尽管如此,我看不出有什么具体的线程问题。

我认为在成员函数中使用静态变量比静态成员更好。 一旦调用方法,将创建此“\u实例”

HandlerOpenGL& HandlerOpenGL::GetInstance(void)
{
    static HandlerOpenGL _instance;
    return _instance;
}

请看

专业提示-如果您确实认为需要单例,请为其提供值语义,并将其单例特性封装为私有实现细节

将来你可能会意识到你根本不需要单身。如果您使用值语义编写类,则不必更改任何用户代码,只需更改singleton的私有实现:

struct HandlerOpenGL
{
    // public interface

    // note - allow it to be copyable.

    // interface exists as instance method
    void do_some_graphics();

private:
    struct impl;
    // this line is the only clue that singletons are involved
    static auto get_impl() -> impl&;
};

// implementation (private)

struct HandlerOpenGL::impl
{
    // this class holds your static data such as library pointers
    // it can be non-moveable etc

    impl()
    {
        // _gfx_engine = init_gfx_engine();
        // if (!_gfx_engine)
        //   throw std::runtime_error("gfx engine failed to init");
    }

    ~impl()
    {
        // commented out so that this example compiles with no
        // external dependencies
        // if (_gfx_engine)
        //   deinit_gfx_engine(_gfx_engine);
    }

    // gfx_engine * _gfx_engine;

};

// this is the private singleton-fetcher    
auto HandlerOpenGL::get_impl() -> impl&
{
    static impl _;
    return _;
}

// implement the interface

void HandlerOpenGL::do_some_graphics()
{
    // fetch my internal singleton
    auto& static_data = get_impl();

    // use methods and data in static_data here
    // gfx_draw_surface(static_data._gfx_engine);
}

// now you can use it like a value - and it's decoupled from logic

// a function that renders graphics on any appropriate graphics engine
template<class T>
void some_graphic_render(T handler)
{
    handler.do_some_graphics();
}

int main()
{
    auto gfx = HandlerOpenGL();   // created trivially - behaves like a value

    auto gfx2 = gfx;    // copies no problem at all

    // use as an argument to allow ADL - allows decoupling and dependency injection

    some_graphic_render(gfx);
    some_graphic_render(HandlerOpenGL());   // can even create as needed. no performance cost

    return 0;
}
struct HandlerOpenGL
{
//公共接口
//注意-允许其可复制。
//接口作为实例方法存在
void do_一些图形();
私人:
结构impl;
//这条线是关于单身汉的唯一线索
静态自动获取_impl()->impl&;
};
//实施(私人)
结构HandlerOpenGL::impl
{
//此类保存静态数据,如库指针
//它可以是不可移动的等
impl()
{
//_gfx_engine=init_gfx_engine();
//如果(!\gfx\U发动机)
//抛出std::runtime_错误(“gfx引擎初始化失败”);
}
~impl()
{
//注释掉,因此此示例编译时没有
//外部依赖关系
//如果(_gfx_发动机)
//脱硝gfx_发动机(_gfx_发动机);
}
//gfx_发动机*_gfx_发动机;
};
//这是私有的单例抓取程序
自动处理OpenGL::get_impl()->impl&
{
静态impl;
返回;;
}
//实现接口
void handleOpenGL::做一些图形()
{
//获取我的内部单例
自动和静态_data=get_impl();
//在这里使用静态数据中的方法和数据
//gfx_绘制_曲面(静态_数据。_gfx_引擎);
}
//现在你可以像使用一个值一样使用它,它与逻辑是解耦的
//在任何适当的图形引擎上渲染图形的函数
模板
无效某些图形渲染(T处理程序)
{
handler.do_一些图形();
}
int main()
{
auto gfx=HandlerOpenGL();//创建简单-行为类似于值
auto gfx2=gfx;//复制完全没有问题
//用作允许ADL的参数-允许解耦和依赖项注入
一些图形渲染(gfx);
一些图形渲染(HandlerOpenGL());//甚至可以根据需要创建。无性能成本
返回0;
}

您链接的文章有一个严重问题:它声称静态变量不是线程安全的。这是错误的。在C++11中,静态变量的初始化要求是线程安全的。因此,不需要使用
一次呼叫
的所有操作。非常感谢!我读了这篇文章:声称静态变量不是线程安全的,但这是从2004年开始的。我还认为Meyers singleton更容易(更干净),但我认为由于静态变量,它不是线程安全的。现在我明白了:是的,Meyer在C++2011及以上版本中的单例是100%线程安全的。