Contenedores y objetos paralelos

 

Para obtener la documentación más reciente de Visual Studio 2017 RC, consulte Documentación de Visual Studio 2017 RC.

La biblioteca de modelos paralelos (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 las proporcionadas por la biblioteca de plantillas estándar (STL). Por ejemplo, el Concurrency:: concurrent_vector clase es similar a la std:: vector clase, salvo que la concurrent_vector clase le permite anexar elementos en paralelo. Utilice contenedores simultáneos si cuenta con código paralelo que requiere acceso de lectura y escritura en el 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áneo. La combinable clase le 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 compartida o un recurso.

En este tema se describe los siguientes contenedores paralelos y objetos de detalle.

Contenedores simultáneos:

Objetos simultáneos:

El Concurrency:: concurrent_vector clase es una clase de contenedor de secuencia que, al igual que el std:: vector clase, que permite el acceso de forma aleatoria a sus elementos. La concurrent_vector clase permite anexar seguro para simultaneidad y elemento de acceso a las operaciones. Anexar las operaciones no invalidan los iteradores ni punteros existentes. Operaciones de acceso y recorrido de iterador también son seguras para simultaneidad.

Diferencias entre concurrent_vector y vector

La concurrent_vector clase se parece mucho a la vector clase. La complejidad de anexar, el acceso a elementos y operaciones de acceso de iterador en una concurrent_vector objeto son los mismos que para un vector objeto. Los siguientes puntos muestran dónde concurrent_vector difiere de vector:

  • Anexar, acceso de elemento, acceso de iterador y las operaciones de recorrido de iterador en una concurrent_vector objeto son seguras para simultaneidad.

  • Puede agregar elementos al final de una concurrent_vector objeto. La concurrent_vector clase no proporciona el insert método.

  • Un concurrent_vector no utiliza el objeto semántica de transferencia cuando se anexa a él.

  • La concurrent_vector clase no proporciona el erase o pop_back métodos. Al igual que con vector, utilice el Borrar método para quitar todos los elementos de un concurrent_vector objeto.

  • La concurrent_vector clase almacena sus elementos de forma contigua en la memoria. Por lo tanto, no puede utilizar el concurrent_vector clase en todas las formas en que puede usar una matriz. Por ejemplo, para una variable denominada v de tipo concurrent_vector, la expresión &v[0]+2 produce un comportamiento indefinido.

  • La concurrent_vector clase define la grow_by y grow_to_at_least métodos. Estos métodos son similares a los cambiar el tamaño de método, excepto en que son seguras para simultaneidad.

  • Un concurrent_vector objeto no reubica sus elementos cuando se anexa a él o cambiar su tamaño. Esto permite punteros existentes e iteradores sigan siendo válidos durante las operaciones simultáneas.

  • El tiempo de ejecución no define una versión especializada de concurrent_vector de tipo bool.

Operaciones seguras para simultaneidad

Todos los métodos que anexar o aumentan el tamaño de una concurrent_vector de objeto o acceso a un elemento en un concurrent_vector de objetos, que son seguras para simultaneidad. La excepción a esta regla es la resize (método).

La siguiente tabla muestra los comunes concurrent_vector métodos y operadores que son seguras para simultaneidad.

enfinaloperador [ ]
comenzarparte frontalpush_back
Atrásgrow_byrbegin
capacidadgrow_to_at_leastrend
vacíamax_sizetamaño

Las operaciones que el runtime proporciona para la compatibilidad con STL, por ejemplo, reserve, no son seguras para simultaneidad. En la tabla siguiente muestra los métodos y operadores que no son seguras para simultaneidad comunes.

asignarreservar
Borrarcambiar el tamaño
operador =shrink_to_fit

Las operaciones que modifican el valor de los elementos existentes no son seguras para simultaneidad. Usar un objeto de sincronización como un reader_writer_lock objeto sincronizar simultáneas de lectura y escritura al mismo elemento de datos. Para obtener más información acerca de los objetos de sincronización, consulte estructuras de datos de sincronización.

Al convertir código existente que utiliza vector usar concurrent_vector, operaciones simultáneas pueden hacer que el comportamiento de la aplicación para cambiar. Por ejemplo, considere el siguiente programa que realiza simultáneamente dos tareas en un concurrent_vector objeto. La primera tarea anexa los elementos adicionales a un concurrent_vector objeto. La segunda tarea calcula la suma de todos los elementos en el 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;
      }
   );
}

Aunque el end método es seguro para simultaneidad, una llamada simultánea activa a la push_back método hace que el valor devuelto por end a cambiar. El número de elementos que recorre el iterador es indeterminado. Por lo tanto, este programa puede producir 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 de la concurrent_vector objeto deja de ser válido. El comportamiento de un concurrent_vector no está definido el objeto que se encuentra en un estado no válido a menos que se indique lo contrario. Sin embargo, el destructor siempre libera la memoria que asigna el objeto, incluso si el objeto está en un estado no válido.

El tipo de datos de los elementos de vector, T, debe cumplir los requisitos siguientes. De lo contrario, el comportamiento de la concurrent_vector clase es indefinida.

  • No debe iniciar el destructor.

  • Si se produce el constructor predeterminado o de copia, el destructor no se debe declarar utilizando la virtual (palabra clave) y deben trabajar correctamente con memoria inicializada en cero.

[Arriba]

El Concurrency:: concurrent_queue clase, igual que el std:: Queue clase, le permite obtener acceso a sus elementos anteriores y posteriores. La concurrent_queue clase habilita seguro para simultaneidad enqueue y dequeue operaciones. La concurrent_queue clase también proporciona compatibilidad de iterador que no es seguro para simultaneidad.

Diferencias entre concurrent_queue y cola

La concurrent_queue clase se parece mucho a la queue clase. Los siguientes puntos muestran dónde concurrent_queue difiere de queue:

  • Enqueue y dequeue operaciones en un concurrent_queue objeto son seguras para simultaneidad.

  • La concurrent_queue clase proporciona compatibilidad de iterador que no es seguro para simultaneidad.

  • La concurrent_queue clase no proporciona el front o pop métodos. La concurrent_queue clase reemplaza estos métodos mediante la definición de la try_pop método.

  • La concurrent_queue clase no proporciona el back método. Por lo tanto, no puede hacer referencia al final de la cola.

  • La concurrent_queue clase proporciona el unsafe_size método en lugar de la size (método). El unsafe_size método no es seguro para simultaneidad.

Operaciones seguras para simultaneidad

Todos los métodos que ponen o quitan de la cola de un concurrent_queue objeto son seguras para simultaneidad.

La siguiente tabla muestra los comunes concurrent_queue métodos y operadores que son seguras para simultaneidad.

vacíainserción
get_allocatortry_pop

Aunque el empty método es seguro para simultaneidad, una operación simultánea puede hacer que la cola crezca o se reduzca antes de la empty devuelve del método.

En la tabla siguiente muestra los métodos y operadores que no son seguras para simultaneidad comunes.

Borrarunsafe_end
unsafe_beginunsafe_size

Compatibilidad de iterador

El concurrent_queue proporciona iteradores que no son seguras para simultaneidad. Se recomienda usar estos iteradores únicamente para la depuración.

Un concurrent_queue iterador recorre los elementos de la dirección de avance. La siguiente tabla muestra a los operadores que cada iterador admite.

OperadorDescripción
operator ++Avanza al siguiente elemento de la cola. Este operador se sobrecarga para proporcionar semántica previa y posterior al incremento.
operador *Recupera una referencia al elemento actual.
operator ->Recupera un puntero al elemento actual.

[Arriba]

El HYPERLINK "file:///C:\\Users\\thompet\\AppData\\Local\\Temp\\DxEditor\\DduePreview\\Default\\798d7037-df37-4310-858b-6f590bbf6ebf\\HTM\\html\\a217b4ac-af2b-4d41-94eb-09a75ee28622" concurrency::concurrent_unordered_map es una clase de contenedor asociativo que, al igual que el std:: unordered_map clase, que controla una secuencia de longitud variable de elementos de tipo std:: Pair < clave const, Ty >. Considere una asignación no ordenada como un diccionario que puede agregar un par de clave y valor a o buscar un valor por clave. Esta clase es útil cuando tiene varios subprocesos o tareas que deben tener acceso a un contenedor compartido simultáneamente, insertar en él o actualizarlo.

En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_map. Este ejemplo inserta teclas de caracteres en el intervalo ['a', ' i']. Dado que el orden de 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 una asignación y reducir la operación en paralelo, vea Cómo: realizar la asignación y reducir las operaciones en paralelo.

Unordered_map y las diferencias entre concurrent_unordered_map

La concurrent_unordered_map clase se parece mucho a la unordered_map clase. Los siguientes puntos muestran dónde concurrent_unordered_map difiere de unordered_map:

  • El erase, bucket, bucket_count, y bucket_size se denominan métodos unsafe_erase, unsafe_bucket, unsafe_bucket_count, y unsafe_bucket_size, respectivamente. El unsafe_ convención de nomenclatura indica que estos métodos no son seguras para simultaneidad. Para obtener más información acerca de la seguridad de simultaneidad, vea operaciones seguras para simultaneidad.

  • Las operaciones de inserción no invalidan los iteradores ni punteros existentes, ni cambian el orden de los elementos que ya existen en el mapa. Insertar y recorrer las operaciones pueden realizarse simultáneamente.

  • concurrent_unordered_map admite reenviar sólo la iteración.

  • Inserción no invalidan ni actualizar los iteradores devueltos por equal_range. Inserción puede anexar elementos iguales hasta el final del intervalo. El iterador begin apunta a un elemento igual.

Para evitar el interbloqueo, ningún método de concurrent_unordered_map mantiene un bloqueo cuando lo 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 en el mismo valor. Las mejores funciones de hash distribución las claves de manera uniforme en el espacio de código hash.

Operaciones seguras para simultaneidad

La concurrent_unordered_map clase permite operaciones de inserción y acceso a los elementos de seguro para simultaneidad. Las operaciones de inserción no invalidan los iteradores ni punteros existentes. Operaciones de acceso y recorrido de iterador también son seguras para simultaneidad. La siguiente tabla muestra el usado concurrent_unordered_map métodos y operadores que son seguras para simultaneidad.

encountfindkey_eq
beginemptyget_allocatormax_size
cbeginendhash_functionoperador [ ]
cendequal_rangeInsertarsize

Aunque el count método puede llamarse de forma segura de subprocesos en ejecución simultánea, distintos subprocesos pueden recibir resultados diferentes si simultáneamente se inserta un nuevo valor en el contenedor.

En la tabla siguiente muestra los métodos utilizados con frecuencia y operadores que no son seguras para simultaneidad.

clearmax_load_factorrehash
load_factoroperador =intercambio

Además de estos métodos, cualquier método que comienza con unsafe_ también no es seguro para simultaneidad.

[Arriba]

El concurrency::concurrent_unordered_multimap se parece mucho la clase la concurrent_unordered_map clase salvo que permite varios valores asignar a la misma clave. Difiere de concurrent_unordered_map de las maneras siguientes:

En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_multimap. Este ejemplo inserta teclas de caracteres en el intervalo ['a', ' i']. concurrent_unordered_multimap permite una clave para 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]
*/

[Arriba]

El concurrency::concurrent_unordered_set se parece mucho la clase la concurrent_unordered_map clase salvo en que administra valores en lugar de pares de clave y valor. La concurrent_unordered_set clase no proporciona operator[] ni at método.

En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_set. 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]
*/

[Arriba]

El concurrency::concurrent_unordered_multiset se parece mucho la clase la concurrent_unordered_set clase salvo que permite valores duplicados. Difiere de concurrent_unordered_set de las maneras siguientes:

En el ejemplo siguiente se muestra la estructura básica para el uso de concurrent_unordered_multiset. Este ejemplo inserta los valores de caracteres en el intervalo ['a', ' i']. concurrent_unordered_multiset permite un valor aparezca 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]
*/

[Arriba]

El Concurrency:: combinable clase proporciona almacenamiento local de subprocesos reutilizable que permite realizar cálculos específicos y, a continuación, combinar estos cálculos en un resultado final. Puede pensar en un objeto combinable como una variable de reducción.

La combinable clase es útil si tiene un recurso que se comparte entre varios subprocesos o tareas. La combinable clase le ayuda a eliminar el estado compartido al proporcionar acceso a los recursos compartidos de forma libre de bloqueo. Por lo 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 los datos compartidos desde varios subprocesos.

Métodos y características

La siguiente tabla muestra algunos de los métodos importantes de la combinable clase. Para obtener más información acerca de todos los combinable métodos de la clase, vea combinable (clase).

MétodoDescripción
localRecupera una referencia a la variable local que está asociada con el contexto del subproceso actual.
BorrarQuita todas las variables locales del subproceso de la combinable objeto.
combinar

 combine_each
Utiliza la función combine proporcionada para generar un valor final del conjunto de todos los cálculos locales de subproceso.

El combinable es una clase de plantilla que se parametriza en el resultado final combinado. Si se llama al constructor predeterminado, la T tipo de parámetro de plantilla debe tener un constructor predeterminado y un constructor de copias. Si el T tipo de parámetro de plantilla no tiene un constructor predeterminado, llame a la versión sobrecargada del constructor que toma una función de inicialización como su parámetro.

Puede almacenar datos adicionales en un combinable objeto después de llamar a la combinar o combine_each métodos. También puede llamar a la combine y combine_each métodos varias veces. Si ningún valor local en un combinable objeto los cambios, el combine y combine_each métodos producen el mismo resultado cada vez que se les llama.

Ejemplos

Para obtener ejemplos sobre cómo utilizar el combinable de clases, vea los temas siguientes:

[Arriba]

Cómo: usar contenedores paralelos para aumentar la eficacia
Muestra cómo se usan contenedores paralelos para almacenar de forma eficaz y tener acceso a datos en paralelo.

Cómo: usar la clase combinable para mejorar el rendimiento
Muestra cómo utilizar el combinable clase para eliminar el estado compartido y mejorar el rendimiento.

Cómo: usar la clase combinable para combinar conjuntos
Muestra cómo utilizar un combine función para combinar conjuntos de datos locales de subproceso.

Biblioteca de modelos paralelos (PPL)
Describe la biblioteca PPL, que proporciona un modelo de programación imperativo que favorece la escalabilidad y facilidad de uso para desarrollar aplicaciones simultáneas.

Clase concurrent_vector

Clase concurrent_queue

concurrent_unordered_map (clase)

concurrent_unordered_multimap (clase)

concurrent_unordered_set (clase)

concurrent_unordered_multiset (clase)

combinable (clase)

Mostrar: