【问题标题】:c++ singleton implementation STL thread safec++单例实现STL线程安全
【发布时间】:2015-12-24 16:33:35
【问题描述】:

我一直在尝试使用 STL 的一些 C++11 特性来实现单例。我阅读了一些实现,发现这很不错:http://silviuardelean.ro/2012/06/05/few-singleton-approaches/ 我做了一些更改,得到了在 VS2013 上运行的波纹管代码,但我仍然想知道:

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

b) 从 GetInstance 返回 shared_ptr 而不是引用是否可以(良好做法)?

PS.:我的单例是 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;
};

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)
{
}

【问题讨论】:

  • 您链接的文章有一个严重的问题:它声称 静态变量不是线程安全的。这是错误的。在 C++11 中,静态变量的初始化需要是线程安全的。因此,不需要使用call_once 进行所有这些操作。
  • 非常感谢!我读过这篇文章:aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf 声称静态变量不是线程安全的,但它是从 2004 年开始的。

标签: c++ multithreading c++11 stl singleton


【解决方案1】:

我根本看不到使用shared_ptr 的意义。如果是单例,则不会被复制。那么为什么要使用shared_ptr

我也相信,Meyers 单例更容易实现,需要更少的输入,并且不依赖于动态分配,所以我想知道为什么有人会做其他事情。

尽管如此,我没有看到具体的线程问题。

【讨论】:

  • 我也认为 Meyers 单例更容易(也更干净),但我认为由于静态变量,它不是线程安全的。现在我看到了:stackoverflow.com/questions/1661529/…
  • 是的,Meyer 在 C++2011 及更高版本中的单例是 100% 线程安全的。
【解决方案2】:

我认为在成员函数中使用静态变量比使用静态成员更好。 一旦方法被调用,这个“_instance”就会被创建。

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

this

【讨论】:

    【解决方案3】:

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

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

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-12
      • 2011-01-13
      相关资源
      最近更新 更多