std :: shared_ptr seguridad del hilo explicada

106

Estoy leyendo http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html y algunos problemas de seguridad de los subprocesos aún no están claros para mí:

  1. Standard garantiza que el recuento de referencias se maneja con seguridad para subprocesos y es independiente de la plataforma, ¿verdad?
  2. Problema similar: el estándar garantiza que solo un hilo (con la última referencia) llamará a eliminar en el objeto compartido, ¿verdad?
  3. shared_ptr no garantiza ningún hilo de seguridad para el objeto almacenado en él?

EDITAR:

Pseudo código:

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

¿Llamar a reset () en el hilo IV eliminará la instancia anterior de una clase creada en el primer hilo y la reemplazará con una nueva instancia? Además, después de llamar a reset () en el hilo IV, ¿otros hilos verán solo el objeto recién creado?

Mentecato
fuente
24
Bien, bien y bien.
Spraff
16
debe usar en make_sharedlugar denew
qdii

Respuestas:

87

Como han señalado otros, lo ha resuelto correctamente con respecto a sus 3 preguntas originales.

Pero la parte final de tu edición

¿Llamar a reset () en el hilo IV eliminará la instancia anterior de una clase creada en el primer hilo y la reemplazará con una nueva instancia? Además, después de llamar a reset () en el hilo IV, ¿otros hilos verán solo el objeto recién creado?

Es incorrecto. Sólo dse apunte a la nueva A(10), y a, by ccontinuará hasta el punto de que el original A(1). Esto se puede ver claramente en el siguiente breve ejemplo.

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

(Claramente, no me molesté con ningún hilo: eso no influye en el shared_ptr::reset()comportamiento).

La salida de este código es

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

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

Nicu Stiurca
fuente
35
  1. Los correctos shared_ptrutilizan incrementos / decrementos atómicos de un valor de recuento de referencia.

  2. El estándar garantiza que solo un hilo llamará al operador de eliminación en un objeto compartido. No estoy seguro de si especifica específicamente que el último hilo que elimina su copia del puntero compartido será el que llame a eliminar (probablemente, en la práctica, este sería el caso).

  3. No, no es así, el objeto almacenado en él puede ser editado simultáneamente por varios hilos.

EDITAR: Seguimiento leve, si desea tener una idea de cómo funcionan los punteros compartidos en general, es posible que desee consultar la boost::shared_ptrfuente: http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp .

Nada mas
fuente
3
1. Cuando diga "'shared_ptrs' use incrementos / decrementos atómicos de un valor de recuento de referencia". ¿Quiere decir que no usan ningún bloqueo interno para el incremento / decremento atómico, que cambia el contexto? En lenguaje simple, ¿varios subprocesos podrían incrementar / disminuir el recuento de referencias sin usar el bloqueo? ¿Un incremento atómico se realiza mediante instrucciones especiales atomic_test_and_swap / atomic_test_and_increment?
rahul.deshmukhpatil
@rahul el compilador es libre de usar un mutex / lock, pero la mayoría de los buenos compiladores no usarán un mutex / lock en plataformas donde se puede hacer sin bloqueos.
Bernard
@Bernard: ¿quiere decir que depende de la implementación de "compiladores std lib shared_ptr" para la plataforma?
rahul.deshmukhpatil
2
Si. Según tengo entendido, la norma no dice que deba estar libre de candados. Pero en los últimos GCC y MSVC, no tiene bloqueos en el hardware Intel x86, y creo que es probable que otros buenos compiladores hagan lo mismo cuando el hardware lo admita.
Bernard
18

std::shared_ptr no es seguro para subprocesos.

Un puntero compartido es un par de dos punteros, uno al objeto y otro a un bloque de control (manteniendo el contador de referencia, enlaces a punteros débiles ...).

Puede haber múltiples std :: shared_ptr y cada vez que acceden al bloque de control para cambiar el contador de referencia, es seguro para subprocesos, pero el std::shared_ptrmismo NO es seguro para subprocesos ni atómico.

Si asigna un nuevo objeto a un std::shared_ptrtiempo que otro hilo lo usa, podría terminar con el puntero del nuevo objeto pero aún usando un puntero al bloque de control del objeto antiguo => CRASH.

Lothar
fuente
4
Podríamos decir que una sola std::shared_ptrinstancia no es segura para subprocesos. Desde std :: shared_ptr reference: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
Esto podría redactarse mejor. Se std::shared_ptr<T>garantiza que una instancia es segura para subprocesos cuando siempre se usa por valor (copiado / movido) a través de los límites del subproceso. Todos los demás usos std::shared_ptr<T>&no son seguros a través de los límites de los hilos
WhiZTiM