Listings Singleton-Pattern

Listing 1: Der naive Schutz des Singletons mit einem Lock
std::mutex myMutex;

class MySingleton{
public:
    static MySingleton& getInstance(){
        std::lock_guard<std::mutex> myLock(myMutex);
        if (!instance)instance= new MySingleton();  // Zeile 7
        return *instance;
    }
    MySingleton(const MySingleton&) = delete;
    MySingleton& operator=(const MySingleton&) = delete;
private:
    MySingleton() = default;
    ~MySingleton() = default;
    static MySingleton* instance;
};

MySingleton* MySingleton::instance = nullptr;

-----------------------

Listing 2: Das Double-Checked-Locking-Pattern
static MySingleton& getInstance(){
    if (!instance){  // check, Zeile 2
       std::lock_guard<std::mutex> myLock(myMutex);  // lock, Zeile 3
       if(!instance) instance = new MySingleton();  // check, Zeile 4
    }
    return *instance;

}

-----------------------

Listing 3: Das Single-Threaded Singleton als Referenzwert
#include <chrono>
#include <iostream>

constexpr auto tenMill = 10000000;

class MySingleton{
public:
  static MySingleton& getInstance(){  // Zeile 8
    static MySingleton instance;            // Zeile 9
    volatile int dummy{};                         // Zeile 10
    return instance;                                 // Zeile 11
  }                                                              // Zeile 12
private:
  MySingleton() = default;
  ~MySingleton() = default;
  MySingleton(const MySingleton&) = delete;
  MySingleton& operator=(const MySingleton&) = delete;
  
};

int main(){
    
  constexpr auto fourtyMill = 4 * tenMill;
  
  auto begin= std::chrono::system_clock::now();
  
  for ( size_t i = 0; i <= fourtyMill; ++i){
    MySingleton::getInstance();  // Zeile 28
  }
  
  auto end = std::chrono::system_clock::now() - begin;
  
  std::cout << std::chrono::duration<double>(end).count() << std::endl;

}

-----------------------

Listing 4: Das Meyers Singleton
#include <chrono>
#include <iostream>
#include <future>

constexpr auto tenMill = 10000000;

class MySingleton{
public:
  static MySingleton& getInstance(){
    static MySingleton instance;
    volatile int dummy{};
    return instance;
  }
private:
  MySingleton() = default;
  ~MySingleton() = default;
  MySingleton(const MySingleton&) = delete;
  MySingleton& operator=(const MySingleton&) = delete;

};

std::chrono::duration<double> getTime(){  // Zeile 22

  const auto begin = std::chrono::system_clock::now();
  for (size_t i = 0; i <= tenMill; ++i){
      MySingleton::getInstance();
  }
  return std::chrono::system_clock::now() - begin;
  
}  // Zeile 30

int main(){
 
    auto fut1= std::async(std::launch::async, getTime);  // Zeile 34
    auto fut2= std::async(std::launch::async, getTime);
    auto fut3= std::async(std::launch::async, getTime);
    auto fut4= std::async(std::launch::async, getTime);  // Zeile 37
    
    const auto total= fut1.get() + fut2.get() + fut3.get() + fut4.get();  // Zeile 39
    
    std::cout << total.count() << std::endl;

}

-----------------------

Listing 5: Threadsichere Initialisierung des Singletons mit einem Lock
#include <chrono>
#include <iostream>
#include <future>
#include <mutex>

constexpr auto tenMill = 10000000;

std::mutex myMutex;

class MySingleton{
public:
  static MySingleton& getInstance(){
    std::lock_guard<std::mutex> myLock(myMutex);
    if (!instance){
        instance= new MySingleton();
    }
    volatile int dummy{};
    return *instance;
  }
private:
  MySingleton() = default;
  ~MySingleton() = default;
  MySingleton(const MySingleton&) = delete;
  MySingleton& operator=(const MySingleton&) = delete;

  static MySingleton* instance;
};


MySingleton* MySingleton::instance = nullptr;

...

-----------------------

Listing 6: Threadsichere Initialisierung des Singletons mit der Funktion std::call_once und dem Flag std::once_flag
#include <chrono>
#include <iostream>
#include <future>
#include <mutex>
#include <thread>

constexpr auto tenMill = 10000000;

class MySingleton{
public:
  static MySingleton& getInstance(){
    std::call_once(initInstanceFlag, &MySingleton::initSingleton);  // Zeile 12
    volatile int dummy{};
    return *instance;
  }
private:
  MySingleton() = default;
  ~MySingleton() = default;
  MySingleton(const MySingleton&) = delete;
  MySingleton& operator=(const MySingleton&) = delete;

  static MySingleton* instance;
  static std::once_flag initInstanceFlag;

  static void initSingleton(){
    instance= new MySingleton;
  }
};

MySingleton* MySingleton::instance = nullptr;
std::once_flag MySingleton::initInstanceFlag;

...

-----------------------


Listing 7: Threadsichere Initialisierung des Singletons mit atomaren Variablen (sequenzielle Konsistenz)
#include <atomic>
#include <iostream>
#include <future>
#include <mutex>
#include <thread>

constexpr auto tenMill = 10000000;

class MySingleton{
public:
  static MySingleton* getInstance(){
    MySingleton* sin = instance.load();  // Zeile 12
    if (!sin){
      std::lock_guard<std::mutex> myLock(myMutex);
      sin = instance.load(std::memory_order_relaxed); // Zeile 15
      if(!sin){
        sin = new MySingleton(); // Zeile 17
        instance.store(sin); // Zeile 18
      }
    }
    volatile int dummy{};
    return sin;
  }
private:
  MySingleton() = default;
  ~MySingleton() = default;
  MySingleton(const MySingleton&) = delete;
  MySingleton& operator=(const MySingleton&) = delete;

  static std::atomic<MySingleton*> instance;
  static std::mutex myMutex;
};


std::atomic<MySingleton*> MySingleton::instance;
std::mutex MySingleton::myMutex;

...

-----------------------

Listing 8: Threadsichere Initialisierung des Singletons mit atomaren Variablen (Acquire-Release-Semantik)
#include <atomic>
#include <iostream>
#include <future>
#include <mutex>
#include <thread>

constexpr auto tenMill = 10000000;

class MySingleton{
public:
  static MySingleton* getInstance(){
    MySingleton* sin = instance.load(std::memory_order_acquire);
    if (!sin){
      std::lock_guard<std::mutex> myLock(myMutex);
      sin = instance.load(std::memory_order_relaxed);
      if(!sin){
        sin = new MySingleton();
        instance.store(sin, std::memory_order_release);
      }
    }   
    volatile int dummy{};
    return sin;
  }
...
