2010-12-03 12 views
11

Digamos que tengo un vector de N elementos, pero hasta n elementos de este vector tienen datos significativos. Un subproceso actualizador actualiza el elemento nth o n + 1st (luego establece n = n + 1), también comprueba si n está demasiado cerca de N y llama a vector :: resize (N + M) si es necesario. Después de la actualización, el hilo llama a varios hilos secundarios para leer hasta la enésima información y hacer algunos cálculos.Vector de STL y seguridad de hilo

Se garantiza que los hilos secundarios nunca cambien o eliminen datos (de hecho, no se eliminan datos) y el actualizador llama a los niños justo después de que finaliza la actualización.

Hasta ahora no ha ocurrido ningún problema, pero quiero preguntar si puede ocurrir un problema durante la reasignación de vector a un bloque de memoria más grande, si hay algunos subprocesos secundarios de trabajo de la actualización anterior.
¿O es seguro usar vector, ya que no es seguro para subprocesos, en un caso de multiproceso?

EDITAR: Dado que solo se realiza una inserción cuando el actualizador llama a vector :: resize (N + M, 0), ¿hay alguna posible solución a mi problema? Debido al gran rendimiento del vector STL, no estoy dispuesto a reemplazarlo por un vector bloqueable o, en este caso, ¿hay algún vector performant, conocido y sin bloqueos?

Respuesta

19

Quiero preguntar si puede ocurrir un problema durante la reasignación de vector a un bloque de memoria más grande, si hay algunos subprocesos secundarios de trabajo de la actualización anterior.

Sí, esto sería muy malo.

Si está utilizando un contenedor de varios subprocesos y al menos un subproceso puede realizar alguna acción que pueda modificar el estado del contenedor, el acceso al contenedor debe estar sincronizado.

En el caso de std::vector, cualquier cosa que cambie su tamaño (en particular, inserciones y supresiones) cambiar su estado, incluso si no se requiere una reasignación (cualquier inserción o borrado requiere datos de tamaño de contabilidad internos std::vector 's sean actualizados) .


Una solución a su problema sería tener el productor asignar dinámicamente el std::vector y utilizar un std::shared_ptr<std::vector<T> > de sus dueños y administrar este std::shared_ptr a cada uno de los consumidores.

Cuando el productor necesita agregar más datos, puede asignar dinámicamente un nuevo std::vector con un nuevo tamaño más grande y copias de los elementos del antiguo std::vector. Luego, cuando escinde nuevos consumidores o actualiza a los consumidores con los nuevos datos, simplemente debe darles un std::shared_ptr al nuevo std::vector.

+0

@ James McNellis: Sí. Ese es un buen consejo.Puedo hacer la reasignación yo mismo. En realidad, los vectores se envuelven dentro de una clase que contiene un puntero al vector. No es shared_ptr, pero puedo construir fácilmente un nuevo vector más grande, copiar elementos del anterior, eliminarlo. Entonces, ¿cuál es la forma más rápida de copiar un bloque de memoria grande? CopyMemory()? –

+1

¿No sería una solución más simple usar 'std :: deque' en lugar de un vector? Eso evita las reasignaciones por completo, al tiempo que ofrece un rendimiento casi a la par con el vector. – jalf

+0

@jalf: No creo que sea seguro usar un 'std :: deque' porque las reasignaciones no son la única preocupación. No hay garantía de que 'std :: deque :: operator []' no verifique el tamaño ni ninguna otra contabilidad interna del 'deque', por lo que existe la posibilidad de una condición de carrera en la que el consumidor llame al operador' [] ', que lee el estado interno mientras el productor está agregando datos, lo que modifica el estado interno. –

1

¿De qué manera sus trabajadores deciden trabajar en data thread safe? ¿Hay alguna señal entre los trabajadores y el productor? Si no, definitivamente hay un problema por el cual el productor podría hacer que el vector se mueva mientras todavía se está trabajando. Aunque esto podría resolverse trivialmente moviéndose a un std::deque en su lugar (tenga en cuenta que std::deque invalida los iteradores en push_back pero las referencias a los elementos no se ven afectadas).

+0

@stonemetal: No hay señal entre los trabajadores y el productor. ¿Cómo voy a usar Deque? –

+0

@stonemetal: No inserto datos con push_back(). Lo cambio de tamaño y luego llamo a 'vec [n] = X;' ¿Esto importa? –

+0

Depende de cómo distribuya los trabajos a los trabajadores. Usted dice que solo se agrega al final, por lo que podría simplemente usar push_back o redimensionar según sea necesario, luego distribuya el trabajo a los trabajadores por índice que nunca cambia, ya que no empuja ni muestra frontalmente o borra, también podría distribuir trabajo por un bloque de punteros ya que se garantiza que los elementos no se muevan, aunque no se garantiza que sean contiguos, por lo que necesitaría un puntero para cada elemento que necesita un trabajador en lugar de un inicio y una configuración de longitud de estilo de matriz. Los iteradores se invalidan al redimensionar para que no sean una opción. – stonemetal