ser - C++ 11 atómica y recuento de referencia de puntero compartido intrusivo
pensamientos intrusivos agresivos (1)
Estoy escribiendo un puntero compartido intrusivo y estoy usando C ++ 11 <atomic>
facilidades para el contador de referencia. Aquí están los fragmentos relevantes de mi código:
//...
mutable std::atomic<unsigned> count;
//...
void
SharedObject::addReference() const
{
std::atomic_fetch_add_explicit (&count, 1u,
std::memory_order_consume);
}
void
SharedObject::removeReference() const
{
bool destroy;
destroy = std::atomic_fetch_sub_explicit (&count, 1u,
std::memory_order_consume) == 1;
if (destroy)
delete this;
}
memory_order_acquire
comencé con memory_order_acquire
y memory_order_release
pero luego me convencí de que memory_order_consume
debería ser lo suficientemente bueno. Después de una deliberación adicional, me parece que incluso memory_order_relaxed
debería funcionar.
Ahora, la pregunta es si puedo usar memory_order_consume
para las operaciones o si puedo usar un ordenamiento más débil ( memory_order_relaxed
) o si debo usar un ordenamiento más estricto.
void
SharedObject::addReference() const
{
std::atomic_fetch_add_explicit (&count, 1u, std::memory_order_relaxed);
}
void
SharedObject::removeReference() const
{
if ( std::atomic_fetch_sub_explicit (&count, 1u, std::memory_order_release) == 1 ) {
std::atomic_thread_fence(boost::memory_order_acquire);
delete this;
}
}
Desea utilizar atomic_thread_fence
modo que la delete
sea estrictamente después de fetch_sub
. Reference
Cita del texto vinculado:
El aumento del contador de referencia siempre se puede hacer con memory_order_relaxed: las nuevas referencias a un objeto solo pueden formarse a partir de una referencia existente, y pasar una referencia existente de un hilo a otro ya debe proporcionar la sincronización necesaria.
Es importante imponer cualquier acceso posible al objeto en un hilo (a través de una referencia existente) para que ocurra antes de eliminar el objeto en un hilo diferente. Esto se logra mediante una operación de "liberación" después de eliminar una referencia (cualquier acceso al objeto a través de esta referencia obviamente debe haber ocurrido antes) y una operación de "adquisición" antes de eliminar el objeto.
Sería posible usar memory_order_acq_rel para la operación fetch_sub, pero esto resulta en operaciones de "adquisición" innecesarias cuando el contador de referencia aún no llega a cero y puede imponer una penalización de rendimiento.