Comment mettre en œuvre multithread sûr singleton en C++11, sans l'aide de <mutex>
Maintenant que C++11 a le multithreading, je me demandais quelle est la bonne façon de mettre en œuvre paresseux initialisé singleton sans utiliser les mutex(pour des raisons de perf).
Je suis venu avec cela, mais tbh je ne suis pas vraiment bon à l'écriture lockfree code, donc je cherche des meilleures 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;
}
Noter que clear()
est juste pour le test, réel singleton wouldnt ont cette fonction.
- Double Possible de C++ modèle de conception Singleton
Vous devez vous connecter pour publier un commentaire.
C++11 élimine la nécessité pour de verrouillage manuel. Une exécution simultanée doit attendre si une variable locale statique est déjà en cours d'initialisation.
§6.7 [stmt.dcl] p4
En tant que tel, simple d'avoir un
static
fonction comme ceci:Que cela ne fonctionne tout droit en C++11 (tant que le compilateur correctement met en œuvre la partie de la norme, bien sûr).
Bien sûr, la réel réponse correcte est pour pas utiliser un singleton, période.
<mutex>
". 😉uninitialized
,initialized
,initializing
)...gcc
compilateur fait déjà cela. Quelqu'un sait si msvc du compilateur a ajouté ceci à la langue encore?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."
- Je pas d'accord, les singletons ont un but, ils sont galvaudé et mal utilisé beaucoup, mais ils ont un but.InternedString
) qui repose sur une chaîne de hachage pour obtenir le retour à une intégrale. Pour permettre aux clients de définir de telles choses à la portée de fichier, nous devons nous assurer de l'accès global à la table de hachage, et de s'assurer qu'il est construit au cours de CRT initialisation avantstatic const InternedString str = ...;
est initialisé.InternedString
d'être en mesure de l'utiliser pour définir les variables à portée de fichier.. mais il finit par être un cas j'ai souvent à l'appui, que les gens font de toute façon. Donc, ce genre de forces m'en tournant cette table de hachage que ces internés chaînes dépendent en un singleton. La seule façon que je peux penser à éviter c'est-à-hack CRT, l'initialisation de s'assurer que cette table de hachage est construit avant tout les autres variables d'un client peut définir à la portée de fichier... mais qui nécessiterait probablement de l'utilisation de la bibliothèque pour ensuite remplacer le C temps de fonctionnement.Pour moi la meilleure façon de mettre en œuvre un singleton à l'aide de C++11 est:
static Singleton myInstance;
doit être définie dans la mise en œuvre de ne pas l'en-tête.À mon humble avis, la meilleure façon de mettre en œuvre des singletons est avec un "double-vérifier, seul" verrouillage du modèle, que vous pouvez mettre en œuvre de façon portable en C++ 11:
Double-Vérifier Le Verrouillage Est Fixé En C++11
Ce modèle est vite dans le déjà créés cas, ne nécessitant qu'un seul pointeur de comparaison, et en toute sécurité dans le premier cas d'utilisation.
Comme mentionné dans la réponse précédente, C++ 11 garanties de construction-commande de sécurité pour les variables locales statiques Est locale statique de la variable d'initialisation des threads en C++11? de sorte que vous êtes en sécurité à l'aide de ce modèle. Toutefois, Visual Studio 2013 ne prend pas encore en charge 🙁 Voir la "magie de la statique sur la ligne" sur cette page, donc, si vous utilisez VS2013 vous encore besoin de le faire vous-même.
Malheureusement, rien n'est jamais simple. Le exemple de code référencée pour le motif ci-dessus ne peut pas être appelée à partir de CRT initialisation, parce que le static std::mutex a un constructeur, et n'est donc pas la garantie d'être initialisé avant le premier appel pour obtenir le singleton, si l'appel est un effet de bord de la CRT de l'initialisation. Pour obtenir autour de que, vous avez à utiliser, pas un mutex, mais un pointeur-à-mutex, qui est garanti pour être remis à zéro avant le CRT d'initialisation commence. Alors que vous auriez à utiliser std::atomique::compare_exchange_strong de créer et d'utiliser le mutex.
Je suis en supposant que le C++ 11 thread-safe local-statique-l'initialisation de la sémantique de travail, même lorsqu'il est appelé au cours de CRT initialisation.
Donc, si vous avez le C++ 11 thread-safe local-statique-l'initialisation de la sémantique disponible, utilisez-les. Si non, vous avez du travail à faire, encore plus si vous voulez que votre singleton être thread-safe pendant CRT initialisation.
Il est difficile de lire votre approche que vous n'êtes pas en utilisant le code comme prévu... c'est le modèle commun pour un singleton est l'appel de
instance()
pour obtenir l'instance unique, puis l'utiliser (d'ailleurs, si vous voulez vraiment un singleton, aucun constructeur doit être publique).En tout cas, je ne pense pas que votre approche est sûr, considérer que deux threads tentent d'acquérir le singleton, la première qui se fait de mise à jour de l'indicateur sera le seul de l'initialisation, mais le
initialize
fonction de la sortie précoce sur le second, et le thread pourrait continuer à utiliser le singleton avant le premier thread eu le temps de terminer l'initialisation.La sémantique de votre
initialize
sont cassés. Si vous essayez de décrire /document le comportement de la fonction, vous aurez du plaisir, et va se retrouver décrivant la mise en œuvre plutôt que d'une simple opération. Documentation est généralement un moyen simple pour vérifier une conception/algorithme: si vous vous retrouvez décrivant comment plutôt que ce, alors vous devriez revenir à la conception. En particulier, il n'y a aucune garantie qu'aprèsinitialize
complète de l'objet a effectivement été initialisé (seulement si la valeur retournée esttrue
, et parfois si c'estfalse
, mais pas toujours).Cette version est conforme à concurrence libre où c++11 norme n'est pas garantie à 100% pris en charge. Il offre également une souplesse qui permet d'instancier la "propriété" de l'instance.
Même si la magie statique mot est assez en c++11 et plus le développeur peut avoir la nécessité d'obtenir beaucoup plus de contrôle sur la création de l'instance.