c++ - smart - unique pointer
std:: shared_ptr thread safety (4)
He leído eso
"Múltiples hilos pueden leer y escribir simultáneamente diferentes objetos shared_ptr, incluso cuando los objetos son copias que comparten propiedad". ( MSDN: seguridad de subprocesos en la biblioteca estándar de C ++ )
¿Eso significa que cambiar el objeto shared_ptr es seguro?
Para una instancia, ¿el siguiente código se considera seguro?
shared_ptr<myClass> global = make_shared<myClass>();
...
//In thread 1
shared_ptr<myClass> private = global;
...
//In thread 2
global = make_shared<myClass>();
...
¿Puedo estar seguro de que ese subproceso 1 private
tendrá el valor original de global
o el nuevo valor que el subproceso 2 asignó, pero de cualquier forma tendrá un shared_ptr válido para myClass?
== EDITAR ==
Solo para explicar mi motivación. Quiero tener un puntero compartido para mantener mi configuración y tengo un grupo de subprocesos para manejar las solicitudes.
tan global
es la configuración global.
thread 1
está tomando la configuración actual cuando comienza a gestionar una solicitud.
thread 2
está actualizando la configuración. (solo se aplica a solicitudes futuras)
Si es trabajo, puedo actualizar la configuración de esa manera sin romperla en el medio de un manejo de solicitud.
Las operaciones de lectura no están sujetas a carreras de datos entre ellas, por lo tanto, es seguro compartir la misma instancia de shared_ptr entre hilos, siempre que todos los hilos usen solo métodos const (esto incluye la creación de copias). Tan pronto como un hilo utiliza el método no const (como en "apuntar a otro objeto"), dicho uso ya no es seguro para subprocesos.
El ejemplo de OP no es seguro para subprocesos y requeriría el uso de carga atómica en el subproceso 1. y el almacenamiento atómico en el subproceso 2 (sección 2.7.2.5 en C ++ 11) para que sea seguro para subprocesos.
La palabra clave en el texto de MSDN es de hecho diferentes objetos shared_ptr , como ya se indicó en las respuestas anteriores.
Lo que estás leyendo no significa lo que piensas que significa. Antes que nada, prueba la página shared_ptr para shared_ptr .
Desplácese hacia abajo en la sección "Observaciones" y obtendrá la esencia del problema. Básicamente, un shared_ptr<>
apunta a un "bloque de control" que es la forma en que realiza un seguimiento de cuántos objetos shared_ptr<>
apuntan al objeto "Real". Entonces cuando haces esto:
shared_ptr<int> ptr1 = make_shared<int>();
Si bien solo hay 1 llamada para asignar memoria aquí a través de make_shared
, existen dos bloques "lógicos" que no debería tratarse igual. Uno es el int
que almacena el valor real, y el otro es el bloque de control, que almacena todo el shared_ptr<>
"magic" que lo hace funcionar.
Solo el bloque de control es seguro para subprocesos.
Puse eso en su propia línea para enfatizar. El contenido de shared_ptr
no es seguro para subprocesos ni escribe en la misma instancia shared_ptr
. Aquí hay algo para demostrar lo que quiero decir:
// In main()
shared_ptr<myClass> global_instance = make_shared<myClass>();
// (launch all other threads AFTER global_instance is fully constructed)
//In thread 1
shared_ptr<myClass> local_instance = global_instance;
Esto está bien, de hecho, puedes hacer esto en todos los hilos tanto como quieras. Y luego, cuando local_instance
se destruye ( local_instance
del alcance), también es seguro para subprocesos. Alguien puede acceder a la global_instance
y no hará la diferencia. El fragmento que extrajo de msdn significa básicamente que "el acceso al bloque de control es seguro para la ejecución de subprocesos", por lo que otras instancias shared_ptr<>
se pueden crear y destruir en diferentes subprocesos tanto como sea necesario.
//In thread 1
local_instance = make_shared<myClass>();
Esto esta bien. Afectará el objeto global_instance
, pero solo indirectamente. El bloque de control al que apunta se reducirá, pero se realizará de forma segura para la ejecución de subprocesos. local_instance
ya no señalará el mismo objeto (o bloque de control) como global_instance
hace la global_instance
.
//In thread 2
global_instance = make_shared<myClass>();
Esto es casi seguro que no está bien si se accede a la global_instance
desde cualquier otro subproceso (que usted dice que está haciendo). Necesita un bloqueo si está haciendo esto porque está escribiendo dondequiera que viva la global_instance
, no solo leyendo de ella. Por lo tanto, escribir en un objeto desde múltiples hilos es malo a menos que lo hayas guardado a través de un candado. De modo que puede leer desde el objeto global_instance
asignando nuevos objetos shared_ptr<>
pero no puede escribir en él.
// In thread 3
*global_instance = 3;
int a = *global_instance;
// In thread 4
*global_instance = 7;
El valor de a
no está definido. Podría ser 7, o podría ser 3, o podría ser cualquier otra cosa también. La seguridad de subprocesos de las instancias shared_ptr<>
solo se aplica a la administración de instancias shared_ptr<>
que se inicializaron entre sí, no a lo que apuntan.
Para enfatizar lo que quiero decir, mira esto:
shared_ptr<int> global_instance = make_shared<int>(0);
void thread_fcn();
int main(int argc, char** argv)
{
thread thread1(thread_fcn);
thread thread2(thread_fcn);
...
thread thread10(thread_fcn);
chrono::milliseconds duration(10000);
this_thread::sleep_for(duration);
return;
}
void thread_fcn()
{
// This is thread-safe and will work fine, though it''s useless. Many
// short-lived pointers will be created and destroyed.
for(int i = 0; i < 10000; i++)
{
shared_ptr<int> temp = global_instance;
}
// This is not thread-safe. While all the threads are the same, the
// "final" value of this is almost certainly NOT going to be
// number_of_threads*10000 = 100,000. It''ll be something else.
for(int i = 0; i < 10000; i++)
{
*global_instance = *global_instance + 1;
}
}
Un shared_ptr<>
es un mecanismo para garantizar que los múltiples propietarios de objetos se aseguren de que un objeto esté destruido, no un mecanismo para garantizar que varios hilos puedan acceder a un objeto correctamente. Aún necesita un mecanismo de sincronización separado para usarlo de manera segura en varios hilos (como std::mutex ).
La mejor manera de pensar en ello es que IMO shared_ptr<>
se asegura de que las copias múltiples que apuntan a la misma memoria no tengan problemas de sincronización por sí mismas , pero no hace nada por el objeto apuntado. Trátelo así.
Para agregar a lo que Kevin escribió, la especificación C ++ 14 tiene soporte adicional para el acceso atómico a los objetos shared_ptr en sí mismos:
20.8.2.6 acceso atómico shared_ptr [util.smartptr.shared.atomic]
El acceso concurrente a un objeto
shared_ptr
desde múltiples hilos no introduce una carrera de datos si el acceso se realiza exclusivamente a través de las funciones en esta sección y la instancia se pasa como su primer argumento.
Entonces si lo haces:
//In thread 1
shared_ptr<myClass> private = atomic_load(&global);
...
//In thread 2
atomic_store(&global, make_shared<myClass>());
...
será seguro para hilos.
Significa que tendrá un shared_ptr
válido y un conteo de referencias válido.
Está describiendo una condición de carrera entre 2 hilos que intentan leer / asignar a la misma variable.
Debido a que este es un comportamiento indefinido en general (solo tiene sentido en el contexto y el calendario del programa individual) shared_ptr
no maneja eso.