Pregunta

¿Cómo se implementa una sistema de conteo de referencias eficiente y seguro para subprocesos en CPU X86 en el lenguaje de programación C++?

Siempre me encuentro con el problema de que el operaciones críticas no atómicas, y las operaciones de bloqueo X86 disponibles no son suficientes para implementar el sistema de conteo de referencias.

El siguiente artículo cubre este tema, pero requiere instrucciones especiales de CPU:

http://www.ddj.com/architect/184401888

¿Fue útil?

Solución

Hoy en día, puede utilizar el puntero inteligente Boost/TR1shared_ptr<> para mantener sus referencias contadas.

Funciona genial;sin problemas, sin complicaciones.La claseshared_ptr<> se encarga de todos los bloqueos necesarios en el recuento.

Otros consejos

En VC++, puedes usar _InterlockedCompareExchange.

do
   read the count
   perform mathematical operation
   interlockedcompareexchange( destination, updated count, old count)
until the interlockedcompareexchange returns the success code.

En otras plataformas/compiladores, utilice el intrínseco apropiado para la instrucción LOCK CMPXCHG que expone _InterlockedCompareExchange de MS.

Estrictamente hablando, deberá esperar hasta C++ 0x para poder escribir código seguro para subprocesos en C++ puro.

Por ahora, puede usar Posix o crear sus propios contenedores independientes de plataforma para comparar e intercambiar y/o incrementos/decrementos entrelazados.

Win32 InterlockedIncrementAcquire e InterlockedDecrementRelease (si desea estar seguro y preocuparse por las plataformas con posible reordenamiento, por lo tanto, debe emitir barreras de memoria al mismo tiempo) o InterlockedIncrement e InterlockedDecrement (si está seguro de que permanecerá en x86), son atómicos y Hacer el trabajo.

Dicho esto, Boost/TR1shared_ptr<> se encargará de todo esto por usted, por lo tanto, a menos que necesite implementarlo por su cuenta, probablemente hará lo mejor que pueda para cumplirlo.

Tenga en cuenta que el bloqueo es muy costoso y ocurre cada vez que pasa objetos entre punteros inteligentes, incluso cuando el objeto pertenece actualmente a un subproceso (la biblioteca de punteros inteligentes no lo sabe).

Teniendo esto en cuenta, puede haber una regla general aplicable aquí (¡me alegra que me corrijan!)

Si las siguientes cosas se aplican a usted:

  • Tiene estructuras de datos complejas para las que sería difícil escribir destructores (o donde la semántica de valores de estilo STL sería inapropiada, por diseño), por lo que necesita punteros inteligentes para hacerlo por usted, y
  • Estás utilizando varios hilos que comparten estos objetos y
  • Te preocupas tanto por el rendimiento como por la corrección.

...entonces la recolección de basura real puede ser una mejor opción.Aunque GC tiene mala reputación en cuanto a rendimiento, todo es relativo.Creo que se compara muy favorablemente con el bloqueo de punteros inteligentes.Fue una parte importante de por qué el equipo de CLR eligió GC verdadero en lugar de algo que utilizara el recuento de referencias.Ver Este artículo, en particular esta cruda comparación de lo que significa la asignación de referencia si estás contando:

sin recuento de referencias:

a = b;

conteo de árbitros:

if (a != null)
    if (InterlockedDecrement(ref a.m_ref) == 0)
            a.FinalRelease();

if (b != null)
    InterlockedIncrement(ref b.m_ref);

a = b;

Si la instrucción en sí no es atómica, entonces debe convertir la sección de código que actualiza la variable apropiada en una sección crítica.

es decir. Debe evitar que otros subprocesos ingresen a esa sección de código utilizando algún esquema de bloqueo.Por supuesto, los bloqueos deben ser atómicos, pero puede encontrar un mecanismo de bloqueo atómico dentro de la clase pthread_mutex.

La cuestión de la eficiencia:La biblioteca pthread es tan eficiente como puede ser y aún garantiza que el bloqueo mutex sea atómico para su sistema operativo.

Es caro:Probablemente.Pero todo lo que requiere garantía tiene un coste.

Ese código particular publicado en ese artículo de ddj agrega complejidad adicional para tener en cuenta los errores en el uso de punteros inteligentes.

Específicamente, si no puede garantizar que el puntero inteligente no cambiará en una asignación a otro puntero inteligente, lo está haciendo mal o, para empezar, está haciendo algo muy poco confiable.Si el puntero inteligente puede cambiar mientras se asigna a otro puntero inteligente, eso significa que el código que realiza la asignación no es propietario del puntero inteligente, lo cual, para empezar, es sospechoso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top