2011-10-19 15 views
44

En particular, estoy buscando una cola de bloqueo. ¿Hay algo así en C++ 11? Si no, ¿cuáles son mis otras opciones? Realmente ya no quiero bajar al nivel del hilo. Demasiado propenso a errores.¿Hay contenedores concurrentes en C++ 11?

+2

+1, Interesante Q.Scott Meyers preguntó esto en C++ 0x días [aquí] (http://www.velocityreviews.com/forums/t732306-concurrent-containers.html). Sería interesante saber cómo esto ha cambiado después de C++ 11. –

+0

Muy fácil convertir una cola estándar en una cola de bloqueo usando las primitivas –

Respuesta

32

According to Diego Dagum from Microsoft's Visual C++ Team:

Una pregunta recurrente (así, uno de los muchos) es aproximadamente contenedores STL y si son seguros para subprocesos.

Tomando las palabras de Stephan aquí, la realidad es que no lo son, no como un error sino como una característica: tener todas las funciones de cada miembro de STL contenedor de adquirir un bloqueo interno aniquilaría rendimiento. Como una biblioteca de uso general altamente reutilizable, en realidad no sería que proporcione corrección: el nivel correcto para colocar cerraduras es determinado por lo que está haciendo el programa. En ese sentido, las funciones de miembro individuales no tienden a ser de ese nivel correcto.

The Parallel Patterns Library (PPL) incluye varios recipientes que proporcionan acceso thread-safe a sus elementos:

  • El concurrent_vector Class es una clase de contenedor secuencia que permite el acceso aleatorio a cualquier elemento. Permite anexos seguros de concurrencia, acceso a elementos, acceso a iteradores y operaciones de cruce de iteradores.
  • concurrent_queue Class es una clase de contenedor de secuencia que permite el acceso de "primero en entrar, primero en salir" a sus elementos. Permite un conjunto limitado de operaciones seguras a la concurrencia, como push y try_pop, por nombrar algunas.

Algunas muestras here.

También es interesante: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html.

+0

yeach. pero el problema es - es solo para Windows = ( –

1

Las interfaces de los contenedores simplemente no se han diseñado con este objetivo. Para las interfaces que usan, un bloqueo visible para el cliente es realmente la única forma en que puede lograr esto mientras garantiza la corrección y el comportamiento predecible. También sería terriblemente ineficiente porque la cantidad de adquisiciones sería muy alta (en relación con una buena implementación).

Solución 1

Pass por valor (en su caso).

Solución 2

Crear una colección de simples atornillado de las implementaciones que se pueden utilizar para pasar los contenedores mientras se mantiene un bloqueo de alcance (considerarlo seudo C++):

template <typename TCollection> 
class t_locked_collection { 
public: 
    t_locked_collection(TCollection& inCollection, t_lock& lock) : collection(inCollection), d_lock(lock), d_nocopy() { 
    } 

    TCollection& collection; 
    // your convenience stuff 
private: 
    t_scope_lock d_lock; 
    t_nocopy d_nocopy; 
}; 

entonces la persona que llama empareja el candado con la colección y luego actualiza las interfaces para usar (pasar) el tipo de contenedor donde corresponda. Es solo la extensión de clase de un hombre pobre.

Este contenedor cerrado es un ejemplo simple, y hay algunas otras variantes.Esta es la ruta que elegí porque realmente te permite usar el nivel de granularidad que es ideal para tu programa, aunque no es tan transparente (sintácticamente) como los métodos bloqueados. También es relativamente fácil adaptar los programas existentes. Al menos se comporta de manera predecible, a diferencia de las colecciones con bloqueos internos.

Otra variante sería:

template <typename TCollection> 
class t_lockable_collection { 
public: 
// ... 
private: 
    TCollection d_collection; 
    t_mutex d_mutex; 
}; 

// example: 
typedef t_lockable_collection<std::vector<int> > t_lockable_int_vector; 

... donde un tipo similar a t_locked_collection podría ser utilizado para exponer la colección subyacente. No implica que ese enfoque sea infalible, solo tonto.

+0

con "pasar por valor" porque quiere decir pasar el contenedor completo por valor para crear una copia y trabajar en la copia? ¿O pasar los elementos del contenedor por valor? – steffen

+0

@steffen pasa los elementos del contenedor por valor. Teniendo en cuenta la interfaz que toman muchos contenedores, esto (** Solución 1 **) está lejos de ser una solución óptima. El enfoque también es casi inútil, a menos que esté dispuesto a escribir un Toneladas de manejo de excepciones y uso de una tonelada de punteros compartidos. – justin

8

C++ 11 no proporciona contenedores simultáneos por sí mismo. Sin embargo, hay opciones de biblioteca. Además de la PPL ya mencionada, no olvide la biblioteca Intel TBB.

Tiene una implementación concurrente queue, hash_map, set y vector. Pero no solo es una biblioteca de contenedores segura para subprocesos, sino que también viene con una versión paralela de algoritmos estándar (for-loop, reduce, sort, ...).

Intel TBB website

+1

¿Puede darme el enlace del conjunto simultáneo? – user

2

me sorprende que nadie mencionó moodycamel::ConcurrentQueue. Lo hemos estado utilizando durante bastante tiempo y funciona muy bien. Es específico que su implementación está libre de bloqueos, lo que de inmediato brinda una velocidad enorme. Otras razones para usarlo (citas del sitio oficial):

No hay muchas colas completas y sin bloqueos para C++. Boost tiene uno, pero está limitado a objetos con operadores de asignación triviales y destructores triviales, por ejemplo. La cola TBB de Intel no está bloqueada en y también requiere constructores triviales. Hay muchos artículos académicos que implementan colas sin bloqueos en C++, pero el código fuente utilizable es difícil de encontrar, y lo prueba aún más.

Algunos puntos de referencia y comparaciones están disponibles here, here y here.

+0

El problema con la implementación de moodycamel es que no es FIFO (es decir, no se garantiza que el orden de los elementos revelados sea el mismo que el orden de los elementos empujados) por lo que no es una solución universal. –