web-dev-qa-db-fra.com

std :: shared_ptr thread safety expliqué

Je lis http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html et certains problèmes de sécurité du fil ne sont toujours pas clairs pour moi:

  1. Standard garantit que le comptage des références est géré en toute sécurité et qu'il est indépendant de la plate-forme, n'est-ce pas?
  2. Problème similaire - standard garantit qu'un seul thread (contenant la dernière référence) appellera delete sur un objet partagé, n'est-ce pas?
  3. shared_ptr ne garantit pas la sécurité des threads pour les objets qui y sont stockés?

MODIFIER:

Pseudo code:

// Thread I
shared_ptr<A> a (new A (1));

// Thread II
shared_ptr<A> b (a);

// Thread III
shared_ptr<A> c (a);

// Thread IV
shared_ptr<A> d (a);

d.reset (new A (10));

L'appel de reset () dans le thread IV supprimera l'instance précédente d'une classe créée dans le premier thread et la remplacera par une nouvelle instance? De plus, après avoir appelé reset () dans le thread IV, les autres threads ne verront que le nouvel objet créé?

97
Goofy

Comme d’autres l’ont fait remarquer, vous avez bien compris le contenu de vos 3 questions initiales.

Mais la fin de votre montage

L'appel de reset () dans le thread IV supprimera l'instance précédente d'une classe créée dans le premier thread et la remplacera par une nouvelle instance? De plus, après avoir appelé reset () dans le thread IV, les autres threads ne verront que le nouvel objet créé?

est incorrect. Seul d pointe vers le nouveau A(10), et a, b et c continuera de pointer vers le premier A(1). Cela se voit clairement dans le court exemple suivant.

#include <memory>
#include <iostream>
using namespace std;

struct A
{
  int a;
  A(int a) : a(a) {}
};

int main(int argc, char **argv)
{
  shared_ptr<A> a(new A(1));
  shared_ptr<A> b(a), c(a), d(a);

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  d.reset(new A(10));

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  return 0;                                                                                                          
}

(Clairement, je ne me suis pas soucié de tout thread: cela ne prend pas en compte le comportement shared_ptr::reset().)

La sortie de ce code est

a: 1 b: 1 c: 1 d: 1

a: 1 b: 1 c: 1 d: 10

83
Nicu Stiurca
  1. Correct, shared_ptrs utilise des incréments/décréments atomiques d'une valeur de comptage de référence.

  2. La norme garantit qu'un seul thread appellera l'opérateur de suppression sur un objet partagé. Je ne sais pas s'il spécifie spécifiquement que le dernier thread qui supprime sa copie du pointeur partagé sera celui qui appelle delete (ce qui est probablement le cas dans la pratique).

  3. Non, l'objet qui y est stocké peut être édité simultanément par plusieurs threads.

EDIT: Léger suivi, si vous voulez avoir une idée du fonctionnement des pointeurs partagés en général, vous voudrez peut-être regarder le boost::shared_ptr source: http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp .

34
NothingMore

std::shared_ptr n'est pas thread-safe.

Un pointeur partagé est une paire de deux pointeurs, l'un sur l'objet et l'autre sur un bloc de contrôle (tenant le compteur de références, des liens vers des pointeurs faibles, etc.).

Il peut y avoir plusieurs std :: shared_ptr et chaque fois qu'ils accèdent au bloc de contrôle pour changer le compteur de références, il est thread-safe mais le std::shared_ptr lui-même n'est ni thread-safe ni atomique.

Si vous affectez un nouvel objet à un std::shared_ptr alors qu’un autre thread l’utilise, il peut se retrouver avec le nouveau pointeur d’objet mais en utilisant tout de même un pointeur sur le bloc de contrôle de l’objet ancien => CRASH.

14
Lothar