Contenedores y objetos paralelos
La Biblioteca de modelos de procesamiento paralelo (PPL) incluye varios contenedores y objetos que proporcionan acceso seguro para subprocesos a sus elementos.
Un contenedor simultáneo proporciona acceso seguro para simultaneidad a las operaciones más importantes.La funcionalidad de estos contenedores se parece a la de los contenedores que proporciona la Biblioteca de plantillas estándar (STL).Por ejemplo, la concurrency::concurrent_vector clase es similar a la std:: vector de clase, excepto que la concurrent_vector clase permite añadir elementos en paralelo.Utilice contenedores simultáneos si cuenta con código paralelo que requiere tanto acceso de lectura como de escritura al mismo contenedor.
Un objeto simultáneo se comparte simultáneamente entre los componentes.Un proceso que calcula el estado de un objeto simultáneo en paralelo genera el mismo resultado que otro proceso que calcula el mismo estado en serie.El concurrency::combinable clase es un ejemplo de un tipo de objeto simultáneas.La clase combinable permite realizar cálculos en paralelo y, a continuación, combinar estos cálculos en un resultado final.Utilice objetos simultáneos si usaría un mecanismo de sincronización, por ejemplo, una exclusión mutua, para sincronizar el acceso a una variable o un recurso compartido.
Secciones
En este tema se describen los siguientes contenedores y objetos paralelos en detalle.
Contenedores simultáneos:
Clase concurrent_vector
Diferencias entre concurrent_vector y vector
Operaciones seguras para simultaneidad
Seguridad de las excepciones
Clase concurrent_queue
Diferencias entre concurrent_queue y queue
Operaciones seguras para simultaneidad
Compatibilidad de los iteradores
Clase concurrent_unordered_map
Unordered_map y las diferencias entre concurrent_unordered_map
Operaciones seguras para simultaneidad
Clase concurrent_unordered_multimap
Clase concurrent_unordered_set
Clase concurrent_unordered_multiset
Objetos simultáneos:
Clase combinable
Métodos y características
Ejemplos
Clase concurrent_vector
El concurrency::concurrent_vector es una clase de contenedor de secuencia que, igual que la std:: vector de clases, le permite obtener acceso aleatorio a sus elementos.La clase concurrent_vector permite realizar operaciones de acceso a elementos y anexado seguras para simultaneidad.Las operaciones de anexado no invalidan los punteros ni los iteradores existentes.El acceso a los iteradores y las operaciones de cruce seguro también son seguras para simultaneidad.
Diferencias entre concurrent_vector y vector
La clase concurrent_vector es muy similar a la clase vector.La complejidad de las operaciones de anexar, obtener acceso al elementos y obtener acceso al iterador en un objeto concurrent_vector es igual que en un objeto vector.En los siguientes puntos se muestran las diferencias entre concurrent_vector y vector:
Las operaciones de anexado, acceso a elementos, acceso a iteradores y cruce seguro de iteradores de un objeto concurrent_vector son seguras para simultaneidad.
Solo puede agregar elementos al final de un objeto concurrent_vector.La clase concurrent_vector no proporciona el método insert.
Un objeto concurrent_vector no usa la semántica de transferencia de recursos cuando se le anexa.
La clase concurrent_vector no proporciona los métodos erase ni pop_back.Al igual que ocurre con vector, use el método clear para quitar todos los elementos de un objeto concurrent_vector.
La clase concurrent_vector no almacena sus elementos de forma contigua en la memoria.Por tanto, no puede usar la clase concurrent_vector de todas las maneras en que puede utilizar una matriz.Por ejemplo, en una variable con el nombre v de tipo concurrent_vector, la expresión &v[0]+2 produce un comportamiento indefinido.
La clase concurrent_vector define los métodos grow_by y grow_to_at_least.Estos métodos son similares al método resize, salvo por el hecho de que son seguros para simultaneidad.
Un objeto concurrent_vector no reubica sus elementos cuando se anexa a él o se cambia su tamaño.Esto permite que los punteros e iteradores existentes sigan siendo válidos durante las operaciones simultáneas.
El runtime no define una versión especializada de concurrent_vector para el tipo bool.
Operaciones seguras para simultaneidad
Todos los métodos que anexan a un objeto concurrent_vector o tienen acceso a él, o tienen acceso a un elemento de un objeto concurrent_vector, son seguros para simultaneidad.La excepción a esta regla es el método resize.
En la siguiente tabla se muestran los métodos y operadores comunes de concurrent_vector que son seguros para la simultaneidad.
Las operaciones que el tiempo de ejecución proporciona por compatibilidad con la STL, por ejemplo, reserve, no son seguros para la concurrencia.En la siguiente tabla se muestran los métodos y operadores comunes que son seguros para simultaneidad.
Las operaciones que modifican el valor de elementos existentes no son seguras para simultaneidad.Utilice un objeto de sincronización, como un objeto reader_writer_lock, para sincronizar las operaciones simultáneas de lectura y escritura al mismo elemento de datos.Para obtener más información sobre los objetos de sincronización, vea Estructuras de datos de sincronización.
Al convertir código existente que utiliza vector a código que usa concurrent_vector, las operaciones simultáneas pueden hacer que cambie el comportamiento de la aplicación.Por ejemplo, considere el siguiente programa que realiza simultáneamente dos tareas en un objeto concurrent_vector.La primera tarea anexa elementos adicionales a un objeto concurrent_vector.La segunda tarea calcula la suma de todos los elementos del mismo objeto.
// parallel-vector-sum.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_vector.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create a concurrent_vector object that contains a few
// initial elements.
concurrent_vector<int> v;
v.push_back(2);
v.push_back(3);
v.push_back(4);
// Perform two tasks in parallel.
// The first task appends additional elements to the concurrent_vector object.
// The second task computes the sum of all elements in the same object.
parallel_invoke(
[&v] {
for(int i = 0; i < 10000; ++i)
{
v.push_back(i);
}
},
[&v] {
combinable<int> sums;
for(auto i = begin(v); i != end(v); ++i)
{
sums.local() += *i;
}
wcout << L"sum = " << sums.combine(plus<int>()) << endl;
}
);
}
Si bien el método end es seguro para simultaneidad, una llamada simultánea al método push_back hace que cambie el valor que end devuelve.El número de elementos que recorre el iterador no está determinado.Por tanto, este programa puede generar un resultado diferente cada vez que se ejecuta.
Seguridad de las excepciones
Si una operación de crecimiento o de asignación produce una excepción, el estado del objeto concurrent_vector deja de ser válido.El comportamiento de un objeto concurrent_vector que tiene un estado no válido no está definido, a menos que se indique lo contrario.Sin embargo, el destructor siempre libera la memoria que asigna el objeto, aunque este tenga un estado no válido.
El tipo de datos de los elementos vectoriales, _Ty, debe cumplir los siguientes requisitos.De lo contrario, el comportamiento de la clase concurrent_vector está sin definir.
No se debe iniciar el destructor.
Si se inicia el constructor predeterminado o de copia, el destructor no se debe declarar con la palabra clave virtual y debe funcionar correctamente con memoria inicializada en cero.
Top
Clase concurrent_queue
El concurrency::concurrent_queue de clase, como el std::queue de clases, le permite tener acceso a su parte delantera y copia los elementos.La clase concurrent_queue permite poner en cola y quitar de la cola de forma segura para simultaneidad.La clase concurrent_queue también proporciona compatibilidad de iterador que no es segura para simultaneidad.
Diferencias entre concurrent_queue y queue
La clase concurrent_queue es muy similar a la clase queue.Los siguientes puntos muestran las diferencias entre concurrent_queue y queue:
Las operaciones de poner y quitar de la cola de un objeto concurrent_queue son seguras para simultaneidad.
La clase concurrent_queue proporciona compatibilidad de iterador que no es segura para simultaneidad.
La clase concurrent_queue no proporciona los métodos front ni pop.La clase concurrent_queue reemplaza estos métodos al definir el método try_pop.
La clase concurrent_queue no proporciona el método back.Por tanto, no puede referenciar al final de la cola.
La clase concurrent_queue proporciona el método unsafe_size en lugar del método size.El método unsafe_size no es seguro para simultaneidad.
Operaciones seguras para simultaneidad
Todos los métodos de un objeto concurrent_queue que ponen o quitan de la cola son seguros para simultaneidad.
En la siguiente tabla se muestran los métodos y operadores comunes de concurrent_queue que son seguros para la simultaneidad.
Si bien el método empty es seguro para simultaneidad, una operación simultánea puede hacer que la cola crezca o se reduzca antes de la devolución del método empty.
En la siguiente tabla se muestran los métodos y operadores comunes que son seguros para simultaneidad.
Compatibilidad de los iteradores
concurrent_queue proporciona iteradores que no son seguros para simultaneidad.Se recomienda usar estos iteradores únicamente para la depuración.
Un iterador concurrent_queue solo atraviesa los elementos hacia delante.En la siguiente tabla se muestran a los operadores que cada iterador admite.
Operador |
Descripción |
---|---|
Avanza hasta el siguiente elemento de la cola.Este operador se sobrecarga para proporcionar semántica previa y posterior al incremento. |
|
Recupera una referencia al elemento actual. |
|
Recupera un puntero al elemento actual. |
Top
Clase concurrent_unordered_map
El concurrency::concurrent_unordered_map es una clase de contenedor asociativa que, igual que la std::unordered_map de clases, controles de una secuencia de longitud varía de elementos de tipo std::pair < clave const, Ty >.Considere un mapa desordenado como un diccionario que puede agregar un par de clave y valor a o buscar un valor de clave.Esta clase es útil cuando dispone de varios subprocesos o las tareas que tienen para tener acceso a un contenedor compartido, insertar en él o actualizarlo al mismo tiempo.
En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_map.En este ejemplo inserta las teclas de carácter en el intervalo ['a', ' i'].Dado que el orden de las operaciones es indeterminado, el valor final de cada clave también es indeterminado.Sin embargo, es seguro realizar las inserciones en paralelo.
// unordered-map-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
//
// Insert a number of items into the map in parallel.
concurrent_unordered_map<char, int> map;
parallel_for(0, 1000, [&map](int i) {
char key = 'a' + (i%9); // Geneate a key in the range [a,i].
int value = i; // Set the value to i.
map.insert(make_pair(key, value));
});
// Print the elements in the map.
for_each(begin(map), end(map), [](const pair<char, int>& pr) {
wcout << L"[" << pr.first << L", " << pr.second << L"] ";
});
}
/* Sample output:
[e, 751] [i, 755] [a, 756] [c, 758] [g, 753] [f, 752] [b, 757] [d, 750] [h, 754]
*/
Para obtener un ejemplo que utiliza concurrent_unordered_map para realizar un mapa y reducir la operación en paralelo, consulte Cómo: Realizar operaciones de asignación y reducción en paralelo.
Unordered_map y las diferencias entre concurrent_unordered_map
La clase concurrent_unordered_map es muy similar a la clase unordered_map.En los siguientes puntos se muestran las diferencias entre concurrent_unordered_map y unordered_map:
The erase, bucket, bucket_count, and bucket_size methods are named unsafe_erase, unsafe_bucket, unsafe_bucket_count, and unsafe_bucket_size, respectively.El unsafe_ convención de nomenclatura indica que estos métodos no están seguros de concurrencia.Para obtener más información acerca de la seguridad de la concurrencia, consulte Las operaciones de seguro para la ejecución de simultaneidad.
Las operaciones de inserción no invalidará punteros existentes o los iteradores, ni cambian el orden de los elementos que ya existen en el mapa.Insertar y recorrer las operaciones pueden producirse al mismo tiempo.
concurrent_unordered_mapsoporta reenviar sólo iteración.
Inserción no invalida ni actualizar los iteradores que son devueltos por equal_range.Inserción puede anexar elementos desiguales hasta el final del intervalo.El iterador begin apunta a un elemento igual.
Para ayudar a evitar el interbloqueo, no hay ningún método de concurrent_unordered_map mantiene un bloqueo cuando llama el asignador de memoria, las funciones hash u otro código definido por el usuario.Además, debe asegurarse de que la función hash siempre se evalúa como claves iguales para el mismo valor.Las mejores funciones de hash distribuyen claves uniformemente en el espacio de código hash.
Operaciones seguras para simultaneidad
El concurrent_unordered_map clase permite que las operaciones de inserción y de acceso a elementos de seguro para la ejecución de simultaneidad.Las operaciones de inserción no invalidan punteros existentes o los iteradores.El acceso a los iteradores y las operaciones de cruce seguro también son seguras para simultaneidad.En la tabla siguiente se muestra con frecuencia concurrent_unordered_map los métodos y los operadores que son seguros para la concurrencia.
count |
find |
||
begin |
empty |
get_allocator |
max_size |
cbegin |
end |
hash_function |
|
cend |
equal_range |
size |
Aunque el count método se puede llamar con seguridad de subprocesos en ejecución al mismo tiempo, diferentes subprocesos pueden recibir resultados diferentes al mismo tiempo se inserta un nuevo valor en el contenedor.
En la siguiente tabla muestra los operadores que no son seguros para la concurrencia y métodos utilizados con frecuencia.
clear |
max_load_factor |
rehash |
load_factor |
Además de estos métodos, cualquier método que comienza con unsafe_ también no es seguro para la ejecución de simultaneidad.
Top
Clase concurrent_unordered_multimap
El concurrency::concurrent_unordered_multimap clase se parezca a la concurrent_unordered_map clase salvo que permite que varios valores asignar a la misma clave.También es diferente de concurrent_unordered_map de las siguientes maneras:
El concurrent_unordered_multimap::insert método devuelve un iterador en lugar de std::pair<iterator, bool>.
El concurrent_unordered_multimap no se proporciona la clase operator[] ni la at método.
En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_multimap.En este ejemplo inserta las teclas de carácter en el intervalo ['a', ' i'].concurrent_unordered_multimappermite una clave a tener varios valores.
// unordered-multimap-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
//
// Insert a number of items into the map in parallel.
concurrent_unordered_multimap<char, int> map;
parallel_for(0, 10, [&map](int i) {
char key = 'a' + (i%9); // Geneate a key in the range [a,i].
int value = i; // Set the value to i.
map.insert(make_pair(key, value));
});
// Print the elements in the map.
for_each(begin(map), end(map), [](const pair<char, int>& pr) {
wcout << L"[" << pr.first << L", " << pr.second << L"] ";
});
}
/* Sample output:
[e, 4] [i, 8] [a, 9] [a, 0] [c, 2] [g, 6] [f, 5] [b, 1] [d, 3] [h, 7]
*/
Top
Clase concurrent_unordered_set
El concurrency::concurrent_unordered_set clase se parezca a la concurrent_unordered_map excepto en que administra los valores en lugar de pares de clave y valor de la clase.El concurrent_unordered_set no se proporciona la clase operator[] ni la at método.
En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_set.En este ejemplo inserta los valores de caracteres en el intervalo ['a', ' i'].Es seguro realizar las inserciones en paralelo.
// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
//
// Insert a number of items into the set in parallel.
concurrent_unordered_set<char> set;
parallel_for(0, 10000, [&set](int i) {
set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
});
// Print the elements in the set.
for_each(begin(set), end(set), [](char c) {
wcout << L"[" << c << L"] ";
});
}
/* Sample output:
[e] [i] [a] [c] [g] [f] [b] [d] [h]
*/
Top
Clase concurrent_unordered_multiset
El concurrency::concurrent_unordered_multiset clase se parezca a la concurrent_unordered_set clase salvo que permite que los valores duplicados.También es diferente de concurrent_unordered_set de las siguientes maneras:
El concurrent_unordered_multiset::insert método devuelve un iterador en lugar de std::pair<iterator, bool>.
El concurrent_unordered_multiset no se proporciona la clase operator[] ni la at método.
En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_multiset.En este ejemplo inserta los valores de caracteres en el intervalo ['a', ' i'].concurrent_unordered_multisetpermite a un valor que se producen varias veces.
// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
//
// Insert a number of items into the set in parallel.
concurrent_unordered_multiset<char> set;
parallel_for(0, 40, [&set](int i) {
set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
});
// Print the elements in the set.
for_each(begin(set), end(set), [](char c) {
wcout << L"[" << c << L"] ";
});
}
/* Sample output:
[e] [e] [e] [e] [i] [i] [i] [i] [a] [a] [a] [a] [a] [c] [c] [c] [c] [c] [g] [g]
[g] [g] [f] [f] [f] [f] [b] [b] [b] [b] [b] [d] [d] [d] [d] [d] [h] [h] [h] [h]
*/
Top
Clase combinable
El concurrency::combinable clase proporciona almacenamiento reutilizable, local de subprocesos que le permite realizar cálculos detallados y, a continuación, combinar esos cálculos en un resultado final.Puede considerar un objeto combinable como una variable de reducción.
La clase combinable resulta útil si se dispone de un recurso que se comparte entre varios subprocesos o tareas.La clase combinable ayuda a eliminar el estado compartido ya que proporciona acceso a recursos compartidos sin bloqueos.Por tanto, esta clase proporciona una alternativa al uso de un mecanismo de sincronización, por ejemplo una exclusión mutua, para sincronizar el acceso a datos compartidos de varios subprocesos.
Métodos y características
En la tabla siguiente se muestran algunos de los métodos más importantes de la clase combinable.Para obtener más información acerca de todos los métodos de la clase combinable, vea Clase combinable.
Método |
Descripción |
---|---|
Recupera una referencia a la variable local asociada al contexto del subproceso actual. |
|
Quita todas las variables locales de subproceso del objeto combinable. |
|
Utiliza la función combine proporcionada para generar un valor final a partir del conjunto de todos los cálculos locales de subproceso. |
La clase combinable es una clase de plantilla que se parametriza en el resultado final combinado.Si llama al constructor predeterminado, el tipo de parámetro de plantilla _Ty debe tener un constructor predeterminado y un constructor de copia.Si el tipo de parámetro de plantilla _Ty no tiene un constructor predeterminado, llame a la versión sobrecargada del constructor que toma una función de inicialización como parámetro.
Puede almacenar datos adicionales en un objeto combinable después de llamar a los métodos combine o combine_each.También puede llamar a los métodos combine y combine_each varias veces.Si no cambia ningún valor local de un objeto combinable, los métodos combine y combine_each producen el mismo resultado cada vez que se les llama.
Ejemplos
Para obtener ejemplos acerca de cómo usar la clase combinable, vea los temas siguientes:
Top
Temas relacionados
Cómo: Usar contenedores paralelos para aumentar la eficacia
Se muestra cómo se usan contenedores paralelos para almacenar de forma eficaz los datos y tener acceso a ellos en paralelo.Cómo: Usar la clase combinable para mejorar el rendimiento
Se muestra cómo utilizar la clase combinable para eliminar el estado compartido y, así, mejorar el rendimiento.Cómo: Usar la clase combinable para combinar conjuntos
Se muestra cómo utilizar una función combine para combinar conjuntos de datos locales de subproceso.Parallel Patterns Library (PPL)
Se describe la biblioteca PPL, que proporciona un modelo de programación imperativo que promueve la escalabilidad y facilidad de uso para desarrollar aplicaciones simultáneas.
Referencia
concurrent_unordered_map (Clase)
concurrent_unordered_multimap (Clase)
concurrent_unordered_set (Clase)