Entièrement thread-safe shared_ptr mise en œuvre
Quelqu'un sait d'un thread-safe shared_ptr
mise en œuvre? E. g. améliorer la mise en œuvre de shared_ptr
est thread-safe pour les cibles (compteurs refcount) et également sans danger pour une utilisation simultanée shared_ptr
instance lit, mais non en écriture ou en lecture/écriture.
(voir Boost docs, des exemples 3, 4 et 5).
Est-il un shared_ptr de mise en œuvre qui est entièrement thread-safe pour shared_ptr
instances?
Étrange qui stimulent les docs disent que:
shared_ptr objets d'offrir le même niveau de sécurité des threads comme des types intégrés.
Mais si vous comparez un pointeur ordinaire (type intégré) pour smart_ptr
, puis en écriture simultanés d'un simple pointeur est thread-safe, mais en écriture simultanés à un smart_ptr
ne l'est pas.
EDIT: je veux dire que sans verrouillage de la mise en œuvre sur l'architecture x86.
EDIT2: Un exemple de cas d'utilisation pour un pointeur intelligent serait là où il y a un nombre de threads de travail de mise à jour globale shared_ptr avec un de leur élément de travail en cours et d'un moniteur de fil qui prend des échantillons aléatoires des éléments de travail. Partagé ptr l'élément de travail jusqu'à ce qu'un autre élément de travail pointeur est assigné (détruisant ainsi le travail précédent article). Le moniteur serait d'obtenir la propriété de l'élément de travail (de ce fait, la prévention de l'élément de travail pour être détruit) par l'attribution à ses propres partagé-ptr. Il peut être fait avec XCHG et de suppression manuelle, mais ce serait bien si une commune-ptr pourrait le faire.
Est un autre exemple où le global partagé-ptr est titulaire d'un "processeur", et il est attribué par un thread, et utilisé par un autre thread. Lorsque la "utilisateur" thread voit que le processeur de fragment-ptr est NULL, il utilise une autre logique pour le traitement. Si ce n'est pas NULL, il empêche le processeur de la destruction par l'attribution à ses propres partagé-ptr.
Au moins sur x86, si le pointeur est correctement aligné, l'opération d'écriture est atomique.
Qu'en écriture simultanés dans un thread et de supprimer dans un autre? (supprimer est essentiellement un type particulier de l'écriture; l'un qui oblitère le point d'être souligné).
Max: ce que vous décrivez est une simultané de la lecture et de l'écriture. suppression ne modifie pas la valeur du pointeur de la variable elle-même, donc il ne compte pas comme une écriture -- c'est la -pointu à valeur qui est (potentiellement) écrit (par le destructeur si existe).
"l'opération d'écriture est atomique" Qui n'est pas "thread-safe" dans le bon sens.
OriginalL'auteur Magnus Hiie | 2009-01-14
Vous devez vous connecter pour publier un commentaire.
L'ajout de la nécessaire obstacles à une telle entièrement thread-safe shared_ptr mise en œuvre serait susceptible d'influer sur les performances. Envisager la course suivante (note: pseudo-code abonde):
Thread 1:
global_ptr = A;
Filetage 2:
global_ptr = B;
Fil 3:
local_ptr = global_ptr;
Si l'on se constituantes d'opérations:
Thread 1:
Filetage 2:
Fil 3:
Clairement, si le thread 3 lit le pointeur après Un swap, alors B va et le supprime avant que le nombre de références peut être incrémenté, les mauvaises choses vont arriver.
Pour gérer cela, nous avons besoin d'une valeur factice être utilisé pendant que le thread 3 est en train de faire la refcnt mise à jour:
(note: compare_exchange(variable, attendue, de nouvelles) atomiquement remplace la valeur dans la variable avec de nouveau, si elle est actuellement égal à nouveau, puis renvoie la valeur true si elle l'a fait avec succès)
Thread 1:
Filetage 2:
Fil 3:
Vous avez dû ajouter une boucle, avec atomics dans le milieu de votre /de lecture/de l'opération. Ce n'est pas une bonne chose - et il peut être extrêmement coûteux sur certains Processeurs. Qui plus est, vous vous êtes occupé d'attente. Vous pouvez commencer à obtenir à l'aise avec les futexes et autres joyeusetés - mais à ce stade, vous avez réinventé le verrou.
Ce coût, qui doit être pris en charge par chaque opération, et est de nature très semblable à ce qu'un verrou serait vous donner de toute façon, c'est pourquoi vous n'avez généralement pas voir un tel thread-safe shared_ptr implémentations. Si vous avez besoin d'une telle chose, je vous recommande l'enveloppant d'un mutex et shared_ptr dans une classe utilitaire pour automatiser le verrouillage.
En fait, il serait encore rompre avec juste les fils 1 et 3 - ajouter le thread 2 était juste pour montrer que nous avions déjà initialisé avec quelque chose d'abord.
"l'ajout de barrières aurait un impact sur les performances" !! eh bien, alors vous devriez savoir que les obstacles sont présents, maintenant, à la fois en boost et std::partagé (base_ref_cnt classe). Parce qu'ils utilisent des atomics et atomics faire exécuter le nécessaire load/store rougeur sur le cpu (mem clôture). intrinsèques de soutien par le compilateur et atomique<> du C++11 est encore pire que de CPU clôture, il est également un compilateur clôture (pas de réorganisation de la charge/magasins adopté la atomique). shared_ptr sont lents, aujourd'hui, déjà. Et même pire, ils ne sont pas thread-safe. Ils ne sont que de sécurité si vous n'avez pas de références faibles.
ne sont pas thread-safe" Comment sont-ils dangereux?
parce que vous ne pouvez pas mettre à jour 2 compteurs atomiquement sans l'aide d'un mutex. Et ils n'utilisent atomique swaps/inc/dec, donc je sens il doit y avoir une possibilité de rupture à l'intérieur d'invariants. J'ai mis en œuvre personnalisée
shared_ptr
2 fois et je n'arrivais pas à résoudre ce problème. Peut-être que lestd
les réalisateurs sont des génies bien, je vais avoir besoin de vérifier comment ils le font un jour.OriginalL'auteur bdonlan
Écriture simultanés à un construit-dans le pointeur n'est certainement pas thread-safe. Examiner les implications de l'écriture à la même valeur à l'égard de la mémoire des obstacles si vous voulez vraiment conduire vous-même fou (par exemple, vous pourriez avoir deux fils de la pensée, le même pointeur avaient des valeurs différentes).
RE: Commentaire - la raison built-ins ne sont pas le double de la suppression est parce qu'ils ne sont pas à la suppression de tous (et de la mise en œuvre de boost::shared_ptr j'utilise ne double pas le supprimer, car il utilise un spécial atomique d'incrémentation et de décrémentation, de sorte que seuls les supprimer, mais le résultat aurait pu avoir le pointeur de l'un et de l'ref compter de l'autre. Ou à peu près n'importe quelle combinaison des deux. Il serait mauvais.). La déclaration dans l'élan docs est correct comme il est, vous obtenez les mêmes garanties que vous ne avec une.
RE: EDIT2 - La première situation que vous décrivez sont très différentes entre l'utilisation de built-ins et shared_ptrs. Dans l'un (XCHG et manuel supprimer) il n'y a pas de comptage de référence; vous êtes en supposant que vous êtes le seul et unique propriétaire lorsque vous faites cela. Si l'utilisation partagée des pointeurs, vous dites que les autres threads peuvent avoir la propriété, ce qui rend les choses beaucoup plus complexe. Je crois que c'est possible avec un compare-and-swap, mais ce serait très non-portable.
C++0x est de sortir avec un atomics de la bibliothèque, ce qui devrait le rendre beaucoup plus facile à écrire générique multi-threaded code. Vous aurez probablement à attendre jusqu'à ce que vient de voir une bonne croix-plate-forme de référence des implémentations de thread-safe pointeurs intelligents.
OriginalL'auteur Todd Gardner
Je ne sais pas du tel un pointeur intelligent de mise en œuvre, même si je dois poser la question: comment cela pourrait-il être le comportement utile? La seule scénarios que je peux penser à où vous trouverez simultanée pointeur de mises à jour des conditions de course (c'est à dire les bugs).
Ce n'est pas une critique -- il pourrait être légitime de cas d'utilisation, je ne peux pas penser à elle. S'il vous plaît laissez-moi savoir!
Re: EDIT2
Merci de fournir un couple de scénarios. Il ne sonne comme atomique pointeur écrit serait utile dans ces situations. (Une petite chose: pour le deuxième exemple, quand vous avez écrit "Si ce n'est pas NULL, il empêche le processeur de la destruction par l'attribution à ses propres partagé-ptr", j'espère que vous vouliez dire que vous affectez le global partagé pointeur vers le local pointeur partagé première puis vérifier si le local partagé pointeur est NULL -- comme vous l'avez décrit, elle est sujette à une condition de concurrence où le pointeur partagé devient NULLE après vous tester pour elle et avant de l'affecter à la locale.)
Grâce magnushiie, j'ai commenté votre modifier dans mon post.
"J'espère que vous vouliez dire que vous affectez le global partagé pointeur vers le local pointeur partagé première" -- Oui, j'aurais utilisé une meilleure formulation
OriginalL'auteur j_random_hacker
Vous pouvez utiliser cette application Atomique De Comptage De Référence Des Pointeurs pour au moins mettre en œuvre le mécanisme de calcul de Référence.
OriginalL'auteur Rody
Votre compilateur peut déjà fournir le thread-safe pointeurs intelligents dans les nouvelles Normes C++. Je crois TBB est la planification sur l'ajout d'un pointeur intelligent, mais je ne pense pas que c'est encore comprises. Vous pouvez être en mesure d'utiliser l'un des TBB est thread-safe conteneurs.
OriginalL'auteur
Vous pouvez facilement le faire en incluant un objet mutex avec chaque pointeur partagé, et l'emballage pour incrémenter/décrémenter les commandes avec la serrure.
OriginalL'auteur Unknown
Je ne pense pas que ce si facile, il n'est pas assez pour envelopper votre sh_ptr classes avec un CS. Il est vrai que si vous maintenez un seul CS pour tous les pointeurs partagés il peut faire en sorte d'éviter la mutuelle de l'accès et de suppression des sh_ptr objets entre les différents threads. Mais ce serait terrible, un objet CS pour chaque pointeur partagé serait un véritable goulot d'étranglement. Il serait approprié si tous les wrappable nouveau ptr -s ont différentes CS s", mais de cette façon, nous devons créer notre CS dinamically, et de s'assurer de la copie ctors de sh_ptr classes de transmettre cette Cs. Maintenant, nous sommes arrivés à le même problème: qui quaranties que ce Cs ptr est déjà supprimé ou non. Nous pouvons être un peu plus smarty avec des volatiles m_bReleased drapeaux par exemple, mais de cette façon, nous ne pouvons pas collé les lacunes en matière de sécurité entre la vérification de l'indicateur et l'utilisation partagée de Cs. Je ne vois pas complètement sûr de la résolution de ce problème. Peut-être que terrible global Cs serait le mineur mauvais que le fait de tuer l'application. (désolé pour mon anglais)
OriginalL'auteur
Cela peut ne pas être exactement ce que vous voulez, mais la
boost::atomic
documentation fournit un exemple sur la façon d'utiliser atomique contre avecintrusive_ptr
.intrusive_ptr
est l'un des pointeurs intelligents de Boost, il ne "intrusif de comptage de référence", ce qui signifie que le compteur est "incorporé" dans la cible au lieu de fournir par le pointeur intelligent.Boost
atomic
Exemples D'Utilisation:http://www.boost.org/doc/html/atomic/usage_examples.html
OriginalL'auteur Siu Ching Pong -Asuka Kenji-
À mon avis, la solution la plus simple est d'utiliser un
intrusive_ptr
avec quelques légères (mais nécessaire) de modifications.J'ai partagé mes de mise en œuvre ci-dessous:
http://www.philten.com/boost-smartptr-mt/
intrusive_ptr_release()
, le fil peut être interrompu après que la condition de laif
a évalué mais avant de ledelete
. Un autre thread pourrait alors par exemple appelerintrusive_ptr_add_ref()
etintrusive_ptr_release()
et supprimer le ptr devant le thread d'origine qui se poursuit ensuite comme si rien ne s'était passé et tente de supprimer le ptr .Voir: boost.org/doc/html/atomic/usage_examples.html
OriginalL'auteur Phil Ten