pseint - Cómo implementar el conteo de referencias seguras de subprocesos en C++
programa para elecciones en c++ (7)
¿Cómo se implementa un sistema de conteo de referencias eficiente y sin hilos en las CPU X86 en el lenguaje de programación C ++?
Siempre me encuentro con el problema de que las operaciones críticas no atómicas y las operaciones de enclavamiento X86 disponibles no son suficientes para implementar el sistema de recuento de ref.
El siguiente artículo cubre este tema, pero requiere instrucciones especiales de CPU:
En VC ++, puede 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, use la intrínseca adecuada para la instrucción LOCK CMPXCHG que exhíbe _InterlockedCompareExchange de MS.
Estrictamente hablando, tendrá que esperar hasta C ++ 0x para poder escribir código seguro para subprocesos en C ++ puro.
Por ahora, puede usar Posix, o crear sus propias envolturas independientes de plataforma en comparación e intercambiar y / o aumentar / disminuir.
Hoy en día, puede usar el puntero inteligente Boost / TR1 shared_ptr <> para mantener sus referencias contadas de referencia.
Funciona genial; sin alboroto, sin muss. La clase shared_ptr <> se encarga de todo el bloqueo necesario en el refcount.
Si la instrucción en sí misma no es atómica, entonces necesita hacer que la sección de código que actualiza la variable apropiada sea una sección crítica.
es decir, debe evitar que otros hilos entren en 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 y aún así garantiza que el bloqueo mutex es atómico para su sistema operativo.
¿Es caro? Probablemente. Pero para todo lo que requiere una garantía, hay un costo.
Tenga en cuenta que el bloqueo es muy costoso y ocurre cada vez que entrega objetos entre punteros inteligentes, incluso cuando el objeto pertenece actualmente a un hilo (la biblioteca del puntero inteligente no lo sabe).
Dado esto, puede haber una regla de oro aplicable aquí (¡me complace 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 del valor de estilo STL sería inapropiada, por diseño), por lo que necesita punteros inteligentes para hacerlo por usted, y
- Está utilizando múltiples hilos que comparten estos objetos, y
- Te importa el rendimiento y la corrección
... entonces la recolección de basura real puede ser una mejor opción. A pesar de que GC tiene una mala reputación por el 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ó el verdadero GC en lugar de algo usando el recuento de referencias. Vea este artículo , en particular, esta clara comparación de lo que significa la asignación de referencia si tiene cuenta en curso:
sin recuento de ref:
a = b;
recuento de ref:
if (a != null)
if (InterlockedDecrement(ref a.m_ref) == 0)
a.FinalRelease();
if (b != null)
InterlockedIncrement(ref b.m_ref);
a = b;
Win32 InterlockedIncrementAcquire and InterlockedDecrementRelease (si quiere estar seguro y le importan las plataformas con posible reordenamiento, por lo tanto, necesita emitir barreras de memoria al mismo tiempo) o InterlockedIncrement y InterlockedDecrement (si está seguro de que permanecerá x86), son atómicas y lo harán Hacer el trabajo.
Dicho esto, Boost / TR1 shared_ptr <> manejará todo esto por ti, por lo tanto, a menos que necesites implementarlo por tu cuenta, probablemente harás lo mejor para cumplirlo.
Ese código en particular publicado en ese artículo ddj está agregando complejidad adicional para dar cuenta de los errores en el uso de punteros inteligentes.
Específicamente, si no puede garantizar que el puntero inteligente no cambie en una asignación a otro puntero inteligente, lo está haciendo mal o está haciendo algo muy poco fiable desde el principio. Si el puntero inteligente puede cambiar al ser asignado a otro puntero inteligente, eso significa que el código que realiza la asignación no posee el puntero inteligente, que es sospechoso para empezar.