Question

Dans C ++ et les périls du verrouillage à double vérification, il y a du code Persudo pour implémenter correctement le modèle qui est suggéré par les auteurs. Voir ci-dessous,

Singleton* Singleton::instance () {
    Singleton* tmp = pInstance;
    ... // insert memory barrier (1)
    if (tmp == 0) {
        Lock lock;
        tmp = pInstance;
        if (tmp == 0) {
            tmp = new Singleton;
            ... // insert memory barrier (2)
            pInstance = tmp;
        }
    }
    return tmp;
}

Je me demande simplement si la première barrière de mémoire peut être déplacée juste au-dessus de l'instruction de retour?

ÉDITER: Une autre question: dans l'article lié, comme vidstige cité

Techniquement, vous n'avez pas besoin de barrières bidirectionnelles complètes. La première barrière doit empêcher la migration vers le bas de la construction de Singleton (par un autre fil); La deuxième barrière doit empêcher la migration à la hausse de l'initialisation de Pinstance. Ceux-ci sont appelés opérations «acquérir» et «libération», et peuvent donner de meilleures performances que les barrières complètes sur le matériel (telles que l'itainum) qui fait la distinction.

Il dit que la deuxième barrière n'a pas besoin d'être bidirectionnelle, alors comment peut-elle empêcher que la mission de Pinstance soit déplacée avant cette barrière? Même si la première barrière peut empêcher la migration vers le haut, mais un autre fil peut encore avoir la chance de voir les membres non initialisés.

ÉDITER: Je pense que je comprends presque le but de la première barrière. Comme sonicoder noté, la prédiction de la branche peut entraîner NULL TMP lorsque l'IF renvoie vrai. Pour éviter ce problème, il doit y avoir une barrière acquérir pour empêcher la lecture de TMP en retour avant la lecture dans If.

La première barrière est jumelée avec la deuxième barrière pour atteindre synchroniser avec relation, donc cela peut être descendu.

ÉDITER: Pour ceux qui sont intéressés par cette question, je recommande fortement de lire Memory-Barriers.txt.

Était-ce utile?

La solution

Je n'ai pas vu de bonne réponse ici liée à votre question, donc je décide d'en publier un même après plus de trois ans;)

Je me demande simplement si la première barrière de mémoire peut être déplacée juste au-dessus de l'instruction de retour?

Oui il peut.

C'est pour les threads qui n'entreront pas if déclaration, c'est-à-dire, pInstance a déjà été construit et initialisé correctement, et est visible.

La deuxième barrière (celle juste avant pInstance = tmp;) garantit que l'initialisation des champs des membres de Singleton est attachée à la mémoire avant pInstance = tmp; est engagé. Mais cela ne signifie pas nécessairement que les autres threads (sur d'autres noyaux) verront ces effets de mémoire dans le même ordre (contre-intuitif, non?). Un deuxième thread peut voir la nouvelle valeur du pointeur en cache mais pas encore ces champs membres. Lorsqu'il accède à un membre en dérégérençant le pointeur (par exemple, p->data), l'adresse de ce membre peut déjà être en cache, mais pas celle qui est souhaitée. Pan! Une mauvaise données est lue. Notez que c'est plus que théorique. Il y a des systèmes Vous avez besoin d'effectuer une instruction de cohérence de cache (par exemple, une barrière de mémoire) pour extraire de nouvelles données de la mémoire.

C'est pourquoi la première barrière est là. Il explique également pourquoi il est normal de le placer juste avant le return déclaration (mais ça doit être après Singleton* tmp = pInstance;).

Il dit que la deuxième barrière n'a pas besoin d'être bidirectionnelle, alors comment peut-elle empêcher que la mission de Pinstance soit déplacée avant cette barrière?

Une barrière d'écriture garantit que Chaque écriture précédente se produira effectivement avant que chaque écriture la suive. C'est un panneau d'arrêt, et aucune écriture ne peut le traverser de l'autre côté. Pour une description plus détaillée, reportez-vous à ici.

Autres conseils

Non, la barrière de mémoire ne peut pas être déplacée en dessous de la mise en place d'attribution car la barrière de mémoire protège l'affectation de la migration ascendante. De l'article lié:

La première barrière doit empêcher la migration vers le bas de la construction de Singleton (par un autre fil); La deuxième barrière doit empêcher la migration à la hausse de l'initialisation de Pinstance.

Sur une note latérale: le verrouillage à double vérification des pattes pour les singletons n'est utile que si vous avez d'énormes exigences de performance.

Avez-vous profilé vos binaires et observé l'accès à singleton en tant que cou de bouteille? Sinon, il est probable que vous n'ayez pas besoin de vous déranger du tout avec le modèle de verrouillage à double vérification.

Je recommande d'utiliser une simple serrure.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top