NoSenseEtAl NoSenseEtAl - 3 months ago 23
C++ Question

How to implement multithread safe singleton in C++11 without using <mutex>

Now that C++11 has multithreading I was wondering what is the correct way to implement lazy initialized singleton without using mutexes(for perf reasons).
I came up with this, but tbh Im not really good at writing lockfree code, so Im looking for some better solutions.

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


Note that
clear()
is just for testing, real singleton wouldnt have that function.

Xeo Xeo
Answer

C++11 removes the need for manual locking. Concurrent execution shall wait if a static local variable is already being initialized.

ยง6.7 [stmt.dcl] p4

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

As such, simple have a static function like this:

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

This will work all-right in C++11 (as long as the compiler properly implements that part of the standard, of course).


Of course, the real correct answer is to not use a singleton, period.