2010-08-10 8 views
5

Necesito crear una función que añada un valor a un vector y devuelva el índice del valor que acaba de anexarse.Atomically std :: vector :: push_back() y el índice de retorno

Ejemplo:

int append(std::vector<int>& numbers, int number){ 
    int retval = numbers.size(); 
    // what if some other thread calls push_back(number) in between these calls? 
    numbers.push_back(number); 
    return retval; 
} 

me gustaría hacer esto atómicamente para que el índice devuelto es siempre correcta incluso cuando puede haber múltiples hilos adjuntas valores al vector. Hubiera sido fácil si push_back devolviera el índice del artículo recién agregado. ¿Cómo puedo garantizar que se devuelva el índice correcto?

+5

En realidad, no sería fácil si push_back() devuelve un índice, como push_back() sí no es seguro para subprocesos –

+0

Usted necesita para proteger su contenedor contra todas las escrituras concurrentes. Concurrent plain push_back() ya necesita sincronización vectorial-externa –

+3

Tenga en cuenta que para muchas aplicaciones del mundo real, hacer pequeñas operaciones de seguridad de subprocesos no es la solución correcta: debería considerar proteger trozos de código más grandes.Las colecciones Thread-safe son buenas si las está utilizando como un mecanismo de comunicación. Dicho esto, este podría ser un caso así. –

Respuesta

11

std::vector no tiene incorporado el soporte de subprocesos. Se podría utilizar boost::mutex extenderla:

int append(std::vector<int>& numbers, int number){ 
    boost::mutex::scoped_lock slock(my_lock); 
    int retval = numbers.size(); 
    numbers.push_back(number); 
    return retval; 
} 

que necesita para proteger cualquier operación de lectura/escritura de tal manera. Otra forma es crear una clase contenedora para std::vector que lo extenderá con soporte de subprocesos. Marque this pregunta para más detalles.

+1

¿Cómo funciona esto cuando otro hilo hace algo con el vector que no sea llamar a esta función? Otro hilo que llama a numbers.clear() no va a respetar el scoped_lock aquí. –

+0

Esto funcionará, pero solo asegurará esta operación única. Cualquiera puede hacer un 'push_back()' en otro lugar en el mismo objeto y romperlo. – sharptooth

+4

Es necesario proteger cualquier operación de lectura/escritura. Puede ser OP debería crear una clase de contenedor para 'std :: vector' que lo ampliará con soporte de subprocesos. Verifique [this] (http://stackoverflow.com/questions/1099513/threadsafe-vector-class-for-c) pregunta para más detalles. –

3

Los contenedores STL no son seguros para subprocesos (incluso la llamada al push_back() solo), tendrás que resolver este problema por tu cuenta: utiliza algunas primitivas de sincronización adecuadas fuera de STL.

0

Es necesario utilizar un mutex para garantizar que se devuelve el índice correcto

+0

Esto es realmente un comentario, no una respuesta a la pregunta. Utilice "agregar comentario" para dejar comentarios al autor. –

+2

No estoy de acuerdo. Creo que esta es una respuesta. La respuesta aceptada también da el mismo consejo pero con un ejemplo. –

0

La solución más fuerte de garantía es bloquear todo el vector de todas las operaciones (que significa controlar cada operación de todas partes del código , lo que realmente significa crear un vector sincronizado).

Puede ser que algo tan simple como esto va a hacer para sus propósitos:

int append(std::vector<int>& numbers, int number){ 
    int retval = numbers.size(); 
    // what if some other thread calls push_back(number) in between these calls? 
    numbers.push_back(number); 
    int newSize = numbers.size(); 
    //this bit is as a short-cut in common, easy, cases 
    if(newSize = retval + 1) //no need for further complication 
    return retval; 
    while(++retval < newSize) 
    if(numbers[retval] == number) 
     return retval; 
    //If we get this far, numbers have been deleted, not added. More discussion below. 
} 

Una cosa acerca de esto es que si las discusiones empujan 3, 3, 3, 3, entonces el índice devuelta será incorrecto, aunque seguirá siendo un índice de 3. Si eso está bien o no, depende de tus propósitos.

Otra es que si el vector está elevado o de otro modo acortado, mientras tanto, a continuación, en el mejor de llegar al punto en el que sólo hay que poner un comentario en el código anterior, en errores peor (ya que saltará de nuevo después de que obtenemos newSize, y luego acceder a [retval] deja de ser válido). Debe considerar si este caso puede suceder (quizás sepa por el resto del código que nunca lo hará) y qué hacer si lo hace.

Si las limitaciones de esto son demasiado grandes para su caso de uso, producir un vector totalmente sincronizado es lo mejor que puedo pensar, me temo.

2

En Visual Studio 2010, puede usar un concurrent_vector para esto, ofrece una funcionalidad de crecimiento sincronizado. This topic enumera cada uno de los contenedores concurrentes.

Tenga en cuenta que estos también están disponibles en Intel TBB con sintaxis + semántica idéntica y, como tal, están disponibles en plataforma cruzada.

Cuestiones relacionadas