如何实现多线程安全单在C ++ 11,而无需使用(How to implement multithr

2019-06-17 14:28发布

现在,C ++ 11多线程我想知道什么是执行不使用互斥(用于PERF原因)延迟初始化单的正确方法。 我想出了这个,但TBH我不是在写代码lockfree真的好,所以进出口寻找一些更好的解决方案。

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
# include <atomic>
# include <thread>
# include <string>
# include <iostream>
using namespace std;
class Singleton
{

public:
    Singleton()
    {
    }
static  bool isInitialized()
    {
        return (flag==2);
    }
static  bool initizalize(const string& name_)
    {
        if (flag==2)
            return false;// already initialized
        if (flag==1)
            return false;//somebody else is initializing
        if (flag==0)
        {
            int exp=0;
            int desr=1;
            //bool atomic_compare_exchange_strong(std::atomic<T>* obj, T* exp, T desr)
            bool willInitialize=std::atomic_compare_exchange_strong(&flag, &exp, desr);
            if (! willInitialize)
            {
                //some other thread CASed before us
                std::cout<<"somebody else CASed at aprox same time"<< endl;
                return false;
            }
            else 
            {
                initialize_impl(name_);
                assert(flag==1);
                flag=2;
                return true;
            }
        }
    }
static void clear()
{
    name.clear();
    flag=0;
}
private:
static  void initialize_impl(const string& name_)
{
        name=name_;
}
static  atomic<int> flag;
static  string name;
};
atomic<int> Singleton::flag=0;
string Singleton::name;
void myThreadFunction()
{
    Singleton s;
    bool initializedByMe =s.initizalize("1701");
    if (initializedByMe)
        s.clear();

}
int main()
{
    while (true)
    {
        std::thread t1(myThreadFunction);
        std::thread t2(myThreadFunction);
        t1.join();
        t2.join();
    }
    return 0;
}

需要注意的是clear()是只是为了测试,真正的单身难道不具有的功能。

Answer 1:

C ++ 11消除了手动锁定的需要。 如果静态局部变量已经被初始化的并发执行将等待。

§6.7 [stmt.dcl] p4

如果控制同时进入申报,而变量被初始化,并发执行必须等待初始化完成。

因此,简单的有static像这样的功能:

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

这将工作全就在C ++ 11(只要正确编译器实现课程标准的那部分)。


当然, 真正正确的答案是使用一个单,期 。



Answer 2:

对我来说,最好的方式来使用C ++ 11是实现单:

class Singleton {
 public:
  static Singleton& Instance() {
    // Since it's a static variable, if the class has already been created,
    // it won't be created again.
    // And it **is** thread-safe in C++11.
    static Singleton myInstance;

    // Return a reference to our instance.
    return myInstance;
  }

  // delete copy and move constructors and assign operators
  Singleton(Singleton const&) = delete;             // Copy construct
  Singleton(Singleton&&) = delete;                  // Move construct
  Singleton& operator=(Singleton const&) = delete;  // Copy assign
  Singleton& operator=(Singleton &&) = delete;      // Move assign

  // Any other public methods.

 protected:
  Singleton() {
    // Constructor code goes here.
  }

  ~Singleton() {
    // Destructor code goes here.
  }

 // And any other protected methods.
}


Answer 3:

这是很难理解为你是不是使用的代码如预期你的方法...也就是说,对于一个单身的常见模式是调用instance()来获取一个实例,然后用它(另外,如果你真的想要一个单身,没有构造函数应该是公共的)。

无论如何,我不认为你的做法是安全的,可以考虑两个线程试图获取单身,是可以获得更新的标志将是唯一一个初始化的第一个,但initialize函数将提前退出的第二个,而线程可能会继续使用Singleton 之前的第一个线程得到周围完成初始化。

您的语义initialize都断了。 如果你试图描述 / 文档功能的行为,你将有一些乐趣,并最终将描述实现,而不是通过简单的操作。 归档通常是一个简单的方法来仔细检查设计/算法:如果你最终描述如何 ,而不是什么 ,那么你应该回到设计。 尤其是没有保证后initialize完成对象实际上已经被初始化(仅当返回的值是true ,有时候如果它是false ,但不总是)。



Answer 4:

恕我直言,以实现单例模式的最佳方法是使用“双重检查,单锁定”模式,你可以在C ++ 11可移植性实现: 双检锁固定在C ++ 11这种模式快速在already-创建的情况下,只需要一个单一的指针比较,并在第一用例是安全的。

正如在以前的答复中提到,C ++静态局部变量11个保证施工顺序安全是局部静态变量的初始化线程安全的C ++ 11? 所以你可以使用这个模式是安全的。 但是,Visual Studio的2013还不支持它:-( 看到这个页面上的“神奇静”行 ,所以如果你正在使用VS2013,你仍然需要自己做。

不幸的是,没有什么是以往任何时候都简单。 该示例代码对于上面的图案无法从CRT初始化被称为引用,因为静态的std ::互斥体构造,并因此不能保证第一次调用之前被初始化,以获得单,该呼叫是否是一个副作用CRT初始化的效果。 要解决这个问题 ,你必须使用,而不是一个互斥体,而是一个指针到互斥体,这是保证CRT初始化启动前初始化为零。 然后,你将不得不使用std ::原子:: compare_exchange_strong创建和使用互斥。

我假设的C ++ 11线程安全的地方静态初始化语义当CRT初始化过程中调用甚至工作。

所以,如果你有C ++ 11线程安全的地方静态初始化语义可用,使用它们。 如果没有,你有一些工作要做,即使moreso如果你希望你单身是CRT初始化期间线程安全的。



Answer 5:

template<class T> 
class Resource
{
    Resource<T>(const Resource<T>&) = delete;
    Resource<T>& operator=(const Resource<T>&) = delete;
    static unique_ptr<Resource<T>> m_ins;
    static once_flag m_once;
    Resource<T>() = default;

public : 
    virtual ~Resource<T>() = default;
    static Resource<T>& getInstance() {
        std::call_once(m_once, []() {
            m_ins.reset(new T);
        });
        return *m_ins.get();
    }
};


Answer 6:

#pragma once

#include <memory>
#include <mutex>

namespace utils
{

template<typename T>
class Singleton
{
private:
    Singleton<T>(const Singleton<T>&) = delete;
    Singleton<T>& operator = (const Singleton<T>&) = delete;

    Singleton<T>() = default;

    static std::unique_ptr<T> m_instance;
    static std::once_flag m_once;

public:
    virtual ~Singleton<T>() = default;

    static T* getInstance()
    {
        std::call_once(m_once, []() {
            m_instance.reset(new T);
        });
        return m_instance.get();
    }

    template<typename... Args>
    static T* getInstance2nd(Args&& ...args)
    {
        std::call_once(m_once, [&]() {
            m_instance.reset(new T(std::forward<Args>(args)...));
        });
        return m_instance.get();
    }
};

template<typename T> std::unique_ptr<T> Singleton<T>::m_instance;
template<typename T> std::once_flag Singleton<T>::m_once;

}

此版本的规定是并发自由其中c ++ 11标准,但不保证支持100%。 它还提供了一个灵活的方式来实例化“拥有”的实例。 即使魔术静态字就足以在C ++ 11和更大的开发人员可能有必要的,并得到了实例创建更多的控制。



文章来源: How to implement multithread safe singleton in C++11 without using