117 votes

Dans quelle mesure std::shared_ptr garantit-elle la sécurité des threads?

Je lis http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html et certaines questions de sécurité des threads ne sont pas encore claires pour moi :

  1. La norme garantit-elle que le comptage des références est géré de manière thread-safe et qu'il est indépendant de la plate-forme, n'est-ce pas?
  2. Une question similaire - la norme garantit-elle qu'un seul thread (détenant la dernière référence) appellera delete sur l'objet partagé, n'est-ce pas?
  3. shared_ptr ne garantit aucune sécurité des threads pour l'objet stocké en son sein?

EDIT:

Code pseudo:

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

// Thread II
shared_ptr b (a);

// Thread III
shared_ptr c (a);

// Thread IV
shared_ptr d (a);

d.reset (new A (10));

Appeler reset() dans le thread IV supprimera l'instance précédente de la classe A 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éé?

26 votes

À droite, à droite et à droite.

19 votes

Vous devriez utiliser make_shared au lieu de new

2 votes

En réponse à toutes les questions (depuis l'édition) : juste, juste, juste, faux et faux.

93voto

SchighSchagh Points 3025

Comme d'autres l'ont souligné, vous avez bien compris correctement vos 3 questions originales.

Mais la partie finale de votre édition

Appeler reset() dans le thread IV supprimera l'instance précédente de la classe A 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 incorrecte. Seul d pointera vers le nouveau A(10), et a, b et c continueront de pointer vers l'original A(1). Cela est clairement visible dans l'exemple court suivant.

#include 
#include 
using namespace std;

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

int main(int argc, char **argv)
{
  shared_ptr a(new A(1));
  shared_ptr 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 embêté avec le multi-threading : cela ne rentre pas en compte dans le comportement de 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

38voto

Lothar Points 4740

std::shared_ptr n'est pas thread safe.

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

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

Si vous attribuez un nouvel objet à un std::shared_ptr alors qu'un autre thread l'utilise, cela peut se terminer avec le nouveau pointeur d'objet mais toujours en utilisant un pointeur vers le bloc de contrôle de l'ancien objet => CRASH.

37voto

NothingMore Points 562
  1. Correct, shared_ptrs use atomic increments/decrements of a reference count value.

  2. The standard guarantees only one thread will call the delete operator on a shared object. I am not sure if it specifically specifies the last thread that deletes its copy of the shared pointer will be the one that calls delete (likely in practice this would be the case).

  3. No they do not, the object stored in it can be simultaneously edited by multiple threads.

EDIT: Slight followup, if you want to get an idea of how shared pointers work in general you might want to look at the boost::shared_ptr source: https://www.boost.org/doc/libs/release/boost/smart_ptr/shared_ptr.hpp.

3 votes

1. Quand vous dites " 'shared_ptrs' utilisent des incréments/décréments atomiques d'une valeur de compteur de référence", voulez-vous dire qu'ils n'utilisent aucun verrou interne pour l'incrémentation/décrémentation atomique, ce qui provoque un changement de contexte ? En termes simples, est-ce que plusieurs threads pourraient augmenter/diminuer le compte de référence sans utiliser de verrou ? Et l'incrément atomique est-il effectué par des instructions spéciales atomic_test_and_swap/atomic_test_and_increment ?

0 votes

@rahul Le compilateur est libre d'utiliser un mutex/verrou, mais la plupart des bons compilateurs n'utiliseront pas de mutex/verrou sur des plates-formes où il est possible de le faire sans verrouiller.

0 votes

@Bernard: voulez-vous dire cela dépend de l'implémentation de "shared_ptr" de la bibliothèque standard des compilateurs pour la plateforme?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X