spiegazione della sicurezza del thread std :: shared_ptr


106

Sto leggendo http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html e alcuni problemi di thread safety non sono ancora chiari per me:

  1. Lo standard garantisce che il conteggio dei riferimenti sia gestito thread-safe e sia indipendente dalla piattaforma, giusto?
  2. Problema simile: lo standard garantisce che solo un thread (tenendo l'ultimo riferimento) chiamerà l'eliminazione sull'oggetto condiviso, giusto?
  3. shared_ptr non garantisce alcun thread safety per gli oggetti in esso archiviati?

MODIFICARE:

Pseudo codice:

// 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));

La chiamata a reset () nel thread IV eliminerà l'istanza precedente di una classe creata nel primo thread e la sostituirà con una nuova istanza? Inoltre, dopo aver chiamato reset () nel thread IV, gli altri thread vedranno solo l'oggetto appena creato?


24
Giusto, giusto e giusto.
spruzzo

16
dovresti usare al make_sharedposto dinew
qdii

Risposte:


87

Come altri hanno sottolineato, hai capito correttamente le tue 3 domande originali.

Ma la parte finale della tua modifica

La chiamata a reset () nel thread IV eliminerà l'istanza precedente di una classe creata nel primo thread e la sostituirà con una nuova istanza? Inoltre, dopo aver chiamato reset () nel thread IV, gli altri thread vedranno solo l'oggetto appena creato?

non è corretto. Solo dpunterà alla nuova A(10), e a, be ccontinuerà a punto a quello originale A(1). Questo può essere visto chiaramente nel seguente breve esempio.

#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;                                                                                                          
}

(Chiaramente, non mi sono preoccupato di alcun threading: questo non influisce sul shared_ptr::reset()comportamento.)

L'output di questo codice è

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

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


35
  1. Corretto, shared_ptrusa incrementi / decrementi atomici di un valore di conteggio di riferimento.

  2. Lo standard garantisce che un solo thread chiamerà l'operatore di eliminazione su un oggetto condiviso. Non sono sicuro che specifichi specificamente che l'ultimo thread che cancella la sua copia del puntatore condiviso sarà quello che chiama delete (probabilmente in pratica sarebbe così).

  3. No, l'oggetto in esso memorizzato può essere modificato contemporaneamente da più thread.

EDIT: Leggero seguito, se vuoi avere un'idea di come funzionano i puntatori condivisi in generale, potresti voler guardare la boost::shared_ptrfonte: http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp .


3
1. Quando dici "'shared_ptrs' usa incrementi / decrementi atomici di un valore di conteggio di riferimento". Vuoi dire che non usano alcun blocco interno per l'incremento / decremento atomico, che cambia il contesto? In un linguaggio semplice, più thread possono aumentare / diminuire il conteggio dei riferimenti senza utilizzare il blocco? Un incremento atomico viene eseguito da istruzioni speciali atomic_test_and_swap / atomic_test_and_increment?
rahul.deshmukhpatil

@rahul il compilatore è libero di usare un mutex / lock, ma la maggior parte dei buoni compilatori non userà un mutex / lock su piattaforme dove può essere fatto senza lock.
Bernard

@ Bernard: vuoi dire che dipende dall'implementazione di "compilers std lib shared_ptr" per la piattaforma?
rahul.deshmukhpatil,

2
Sì. Dalla mia comprensione, lo standard non dice che deve essere privo di serratura. Ma negli ultimi GCC e MSVC, è privo di blocchi su hardware Intel x86, e penso che altri buoni compilatori probabilmente faranno lo stesso quando l'hardware lo supporta.
Bernard,

18

std::shared_ptr non è thread-safe.

Un puntatore condiviso è una coppia di due puntatori, uno all'oggetto e uno a un blocco di controllo (tenendo premuto il contatore ref, si collega a puntatori deboli ...).

Possono esserci più std :: shared_ptr e ogni volta che accedono al blocco di controllo per modificare il contatore di riferimento è thread-safe ma lo std::shared_ptrstesso NON è thread-safe o atomico.

Se si assegna un nuovo oggetto a un std::shared_ptraltro thread mentre lo usa, potrebbe finire con il nuovo puntatore all'oggetto ma usando ancora un puntatore al blocco di controllo del vecchio oggetto => CRASH.


4
Potremmo dire che una singola std::shared_ptristanza non è thread-safe. Da riferimento std :: shared_ptr:If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;
JKovalsky

Questo potrebbe essere formulato meglio. Un std::shared_ptr<T>esempio è garantita thread-safe quando sempre utilizzato per valore (copiato / spostato) oltre i limiti del filetto. Tutti gli altri usi std::shared_ptr<T>&non sono sicuri oltre i confini del thread
WhiZTiM
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.