Singleton Synchronization C++

Singleton Synchronization C++

本文关键字:C++ Synchronization Singleton      更新时间:2023-10-16

如果我必须用C++编写一个单例类,我将使用一个静态变量,私有构造函数&返回类对象的公共静态函数。但是,在多线程环境中,代码会出现问题。为了避免多个线程同时访问同一变量,Boost线程是用于同步的最佳机制吗?我的意思是在资源中设置/取消设置锁/互斥。C++标准库中还有其他内置的东西吗?我不必下载boost、构建东西等等?我听说过C++Ox,但不太了解。

C++98/03根本不支持线程。如果您使用的是C++98或03编译器,那么您几乎会使用Boost,或者(或多或少)特定于操作系统的东西,例如pthreads或Win32的线程原语。

C++11有一个相当完整的线程支持库,包括互斥、锁、线程本地存储等。

然而,我觉得有必要指出,最好还是站出来,多想想你是否需要/想要一个辛格尔顿。说得好一点,singleton模式在很大程度上已经失宠了。

编辑:重读这篇文章,我有点跳过了我想说的一件事:至少当我使用它们时,在启动任何辅助线程之前,任何/所有singleton都已完全初始化。这使得初始化过程中对线程安全性的担忧变得毫无意义。我想可能有一个singleton在启动辅助线程之前无法初始化,所以你需要处理它,但至少现在我觉得它是一个非常不寻常的例外,只有在绝对必要的时候我才会处理。

对我来说,使用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.
}

这是一个c++11特性,但通过这种方式,您可以创建线程安全的Singleton。根据新标准,没有必要再关心这个问题了。对象初始化将只由一个线程进行,其他线程将等待它完成。或者您可以使用std::call_once。

如果你想独占访问单例的资源,你必须在这些函数上使用锁。

不同类型的锁:

使用atomic_flg_lck:

class SLock
{
public:
  void lock()
  {
    while (lck.test_and_set(std::memory_order_acquire));
  }
  void unlock()
  {
    lck.clear(std::memory_order_release);
  }
  SLock(){
    //lck = ATOMIC_FLAG_INIT;
    lck.clear();
  }
private:
  std::atomic_flag lck;// = ATOMIC_FLAG_INIT;
};

使用原子:

class SLock
{
public:
  void lock()
  {
    while (lck.exchange(true));
  }
  void unlock()
  {
    lck = true;
  }
  SLock(){
    //lck = ATOMIC_FLAG_INIT;
    lck = false;
  }
private:
  std::atomic<bool> lck;
};

使用互斥:

class SLock
{
public:
  void lock()
  {
    lck.lock();
  }
  void unlock()
  {
    lck.unlock();
  }
private:
  std::mutex lck;
};

仅适用于Windows

class SLock
{
public:
  void lock()
  {
    EnterCriticalSection(&g_crit_sec);
  }
  void unlock()
  {
    LeaveCriticalSection(&g_crit_sec);
  }
  SLock(){
    InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400);
  }
private:
  CRITICAL_SECTION g_crit_sec;
};

原子以及atomic_flg_lck使线程保持自旋计数Mutex只是休眠线程。如果等待时间太长,也许最好休眠线程。最后一个"CRITICAL_SECTION"使线程保持旋转计数,直到消耗了一段时间,然后线程进入睡眠状态。

如何使用这些关键部分?

unique_ptr<SLock> raiilock(new SLock());
class Smartlock{
public:
  Smartlock(){ raiilock->lock(); }
  ~Smartlock(){ raiilock->unlock(); }
};

使用raii成语。构造函数锁定关键部分,析构函数解锁关键部分。

示例

class Singleton {
   void syncronithedFunction(){
      Smartlock lock;
      //.....
   }
}

此实现是线程安全和异常安全的,因为变量锁保存在堆栈中,因此当函数范围结束(函数结束或异常)时,将调用析构函数。

我希望你觉得这很有帮助。

谢谢!!