Jetzt, da C ++ 11 Multithreading hat, habe ich mich gefragt, wie man faul initialisierte Singleton ohne Verwendung von Mutexen (aus Perf-Gründen) richtig implementiert. Ich habe mir das ausgedacht, aber ich bin nicht wirklich gut darin, sperrfreien Code zu schreiben, also suche ich nach besseren Lösungen.
// 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;
}
Beachten Sie, dass clear()
nur zu Testzwecken echte Singleton diese Funktion nicht haben würden.
Antworten:
C ++ 11 macht manuelles Sperren überflüssig. Die gleichzeitige Ausführung muss warten, wenn eine statische lokale Variable bereits initialisiert wird.
§6.7 [stmt.dcl] p4
Als solche haben einfach eine
static
Funktion wie diese:static Singleton& get() { static Singleton instance; return instance; }
Dies funktioniert in C ++ 11 einwandfrei (solange der Compiler diesen Teil des Standards natürlich ordnungsgemäß implementiert).
Die wirklich richtige Antwort ist natürlich, keinen Singleton-Punkt zu verwenden.
quelle
<mutex>
. ;)Of course, the real correct answer is to not use a singleton, period
... :-) ..."Of course, the real correct answer is to not use a singleton, period."
- Ich muss nicht zustimmen, Singletons haben einen Zweck, sie werden häufig überbeansprucht und missbraucht, aber sie haben einen Zweck.Für mich ist der beste Weg, einen Singleton mit C ++ 11 zu implementieren:
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. }
quelle
static Singleton myInstance;
sollte in der Implementierung nicht der Header definiert werden.IMHO ist der beste Weg, Singletons zu implementieren, ein "Double-Check-Single-Lock" -Muster, das Sie portabel in C ++ 11 implementieren können: Double-Checked Locking ist in C ++ 11 behoben behoben. Erstellter Fall, der nur einen einzigen Zeigervergleich erfordert und im ersten Anwendungsfall sicher ist.
Wie in der vorherigen Antwort erwähnt, garantiert C ++ 11 die Sicherheit der Konstruktionsreihenfolge für statische lokale Variablen. Ist die Initialisierung lokaler statischer Variablen in C ++ 11 threadsicher? Sie sind also sicher, wenn Sie dieses Muster verwenden. Visual Studio 2013 unterstützt dies jedoch noch nicht :-( Siehe die Zeile "Magic Statics" auf dieser Seite . Wenn Sie also VS2013 verwenden, müssen Sie dies weiterhin selbst tun.
Leider ist nichts jemals einfach. Der Beispielcode, auf den für das obige Muster verwiesen wird, kann nicht von der CRT-Initialisierung aufgerufen werden, da der statische std :: mutex einen Konstruktor hat und daher nicht garantiert werden kann, dass er vor dem ersten Aufruf initialisiert wird, um den Singleton zu erhalten, wenn der Aufruf ein Side- ist. Effekt der CRT-Initialisierung. Um dies zu umgehen , müssen Sie keinen Mutex verwenden, sondern einen Zeiger auf Mutex, der garantiert vor Beginn der CRT-Initialisierung auf Null initialisiert wird. Dann müssten Sie std :: atomic :: compare_exchange_strong verwenden, um den Mutex zu erstellen und zu verwenden.
Ich gehe davon aus, dass die C ++ 11-Thread-sichere Semantik für die lokale statische Initialisierung auch dann funktioniert, wenn sie während der CRT-Initialisierung aufgerufen wird.
Wenn Sie also die threadsichere lokal-statische Initialisierungssemantik von C ++ 11 zur Verfügung haben, verwenden Sie diese. Wenn nicht, müssen Sie noch einige Arbeiten erledigen, auch wenn Ihr Singleton während der CRT-Initialisierung threadsicher sein soll.
quelle
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 Resource<T>); }); return *m_ins.get(); } };
quelle
Es ist schwierig, Ihren Ansatz zu lesen, da Sie den Code nicht wie beabsichtigt verwenden. Das heißt, das übliche Muster für einen Singleton lautet,
instance()
die einzelne Instanz abzurufen und dann zu verwenden (auch, wenn Sie wirklich einen Singleton möchten, nein Konstruktor sollte öffentlich sein).Auf jeden Fall denke ich nicht, dass Ihr Ansatz sicher ist. Bedenken Sie, dass zwei Threads versuchen, den Singleton zu erfassen. Der erste, der das Flag aktualisiert, ist der einzige, der initialisiert wird, aber die
initialize
Funktion wird beim zweiten früh beendet Eins, und dieser Thread verwendet möglicherweise den Singleton, bevor der erste Thread die Initialisierung abgeschlossen hat.Die Semantik von dir
initialize
ist kaputt. Wenn Sie versuchen, das Verhalten der Funktion zu beschreiben / zu dokumentieren, werden Sie Spaß haben und am Ende eher die Implementierung als eine einfache Operation beschreiben. Die Dokumentation ist normalerweise eine einfache Möglichkeit, ein Design / einen Algorithmus zu überprüfen: Wenn Sie am Ende beschreiben, wie und nicht was , sollten Sie zum Design zurückkehren. Insbesondere gibt es keine Garantie dafür, dassinitialize
das Objekt nach Abschluss tatsächlich initialisiert wurde (nur wenn der zurückgegebene Wert isttrue
und manchmalfalse
, aber nicht immer).quelle
#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; }
Diese Version ist gleichzeitig kostenlos, wenn nicht garantiert wird, dass der C ++ 11-Standard zu 100% unterstützt wird. Es bietet auch eine flexible Möglichkeit, die "eigene" Instanz zu instanziieren. Selbst wenn das magische statische Wort in C ++ 11 und höher ausreicht, muss der Entwickler möglicherweise viel mehr Kontrolle über die Instanzerstellung erlangen.
quelle