2012-02-03 19 views
80

sto leggendo http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html e alcuni problemi di sicurezza thread non sono ancora chiaro per me:std :: shared_ptr filo di sicurezza ha spiegato

  1. garanzie standard che il conteggio di riferimento viene manipolato thread-safe ed è indipendente dalla piattaforma, giusto?
  2. Problema simile: lo standard garantisce che solo un thread (contenente l'ultimo riferimento) chiamerà Elimina su oggetto condiviso, giusto?
  3. shared_ptr non garantisce alcuna sicurezza del thread per l'oggetto memorizzato in esso?

EDIT:

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

Calling reset() in filo di IV eliminerà precedente istanza di una classe creata nel primo thread e sostituirlo con nuova istanza? Inoltre, dopo aver chiamato reset() nel thread IV, altri thread vedranno solo oggetti appena creati?

+23

Destra, destra e destra. – spraff

+14

dovresti usare 'make_shared' invece di' new' – qdii

risposta

67

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

Ma la parte finale della tua modifica

Calling reset() in filo di IV cancellerà precedente istanza di una classe creata nel primo thread e sostituirlo con nuova istanza? Inoltre, dopo aver chiamato reset() nel thread IV, altri thread vedranno solo oggetti appena creati?

non è corretto. Solo d indicherà il nuovo A(10) e a, b e c continuerà a puntare all'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 ho fastidio con qualsiasi filettatura: che non tiene conto del comportamento shared_ptr::reset().)

L'uscita di questo codice è

un: 1 b: 1 c: 1 d: 1

un: 1 b: 1 c: 1 d: 10

28
  1. corretta, shared_ptr s usa atomici incrementi/decrementi di un valore di conteggio di riferimento.

  2. Lo standard garantisce che solo un thread chiamerà l'operatore di eliminazione su un oggetto condiviso. Non sono sicuro se specifica in modo specifico l'ultimo thread che cancella la sua copia del puntatore condiviso sarà quello che chiama delete (probabilmente nella pratica questo sarebbe il caso).

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

EDIT: Lieve followup, se si vuole avere un'idea di come funzionano condivisa puntatori, in generale, si potrebbe desiderare di guardare il sorgente boost::shared_ptr: 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 incrementare/diminuire il conteggio dei riferimenti senza utilizzare il blocco? Un incremento atomico viene eseguito da speciali istruzioni atomic_test_and_swap/atomic_test_and_increment? –

+0

@rahul il compilatore è libero di usare un mutex/lock, ma i migliori compilatori non useranno un mutex/lock su piattaforme dove può essere fatto lock-free. – Bernard

+0

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

5

std :: shared_ptr non è thread-safe.

Un puntatore condiviso è una coppia di due puntatori, uno per l'oggetto e uno per un blocco di controllo (tenendo il contatore ref, collegamenti a puntatori deboli ...).

Ci possono essere più std :: shared_pointer e ogni volta che accedono al blocco di controllo per modificare il contatore di riferimento è thread-safe ma "std :: shared_ptr" non è thread-safe o atomico.

Se si assegna un nuovo oggetto a std :: shared_pointer mentre viene utilizzato da un altro thread, questo potrebbe terminare con il nuovo puntatore all'oggetto ma ancora utilizzando un puntatore al blocco di controllo del vecchio oggetto => CRASH.

Problemi correlati