Share via


Conteneurs et objets parallèles

La Bibliothèque de modèles parallèles (PPL, Parallel Patterns Library) inclut plusieurs conteneurs et objets qui fournissent un accès thread-safe à leurs éléments.

Un conteneur simultané fournit un accès concurrentiel sécurisé aux opérations les plus importantes.Les fonctionnalités de ces conteneurs ressemblent à celles fournies par la Bibliothèque de modèles standard (STL, Standard Template Library).Par exemple, la concurrency::concurrent_vector classe ressemble à la std::vector de classe, sauf que le concurrent_vector classe vous permet d'ajouter des éléments en parallèle.Utilisez des conteneurs simultanés lorsque vous avez du code parallèle qui requiert un accès en lecture et en écriture au même conteneur.

Un objet simultané est partagé simultanément par des composants.Un processus qui calcule l'état d'un objet simultané en parallèle produit le même résultat qu'un autre processus qui calcule le même état de façon séquentielle.Le concurrency::combinable classe est un exemple d'un type d'objet simultanées.La classe combinable vous permet d'effectuer des calculs en parallèle, puis combine ces calculs dans un résultat final.Utilisez des objets simultanés lorsque vous utiliseriez normalement un mécanisme de synchronisation, par exemple un mutex, pour synchroniser l'accès à une variable ou ressource partagée.

Sections

Cette rubrique décrit en détail les conteneurs et objets parallèles suivants.

Conteneurs simultanés :

  • Classe concurrent_vector

    • Différences entre concurrent_vector et vector

    • Opérations à accès concurrentiel sécurisé

    • Sécurité des exceptions

  • Classe concurrent_queue

    • Différences entre concurrent_queue et queue

    • Opérations à accès concurrentiel sécurisé

    • Prise en charge des itérateurs

  • Classe concurrent_unordered_map

    • Différences entre concurrent_unordered_map et les unordered_map

    • Opérations à accès concurrentiel sécurisé

  • Classe concurrent_unordered_multimap

  • Classe concurrent_unordered_set

  • Classe concurrent_unordered_multiset

Objets simultanés :

  • Classe combinable

    • Méthodes et fonctionnalités

    • Exemples

Classe concurrent_vector

Le concurrency::concurrent_vector classe est une classe de conteneur de séquence qui, tout comme le std::vector de classes, vous permet d'accéder à ses éléments au hasard.La classe concurrent_vector permet d'effecteur des opérations d'ajout et d'accès aux éléments sécurisées du point de vue de l'accès concurrentiel.Les opérations d'ajout n'invalident pas les pointeurs ou itérateurs existants.Les opérations de parcourt et d'accès aux itérateurs sont également sécurisées du point de vue de l'accès concurrentiel.

Dd504906.collapse_all(fr-fr,VS.110).gifDifférences entre concurrent_vector et vector

La classe concurrent_vector ressemble étroitement à la classe vector.La complexité des opérations d'ajout, d'accès aux éléments et d'accès aux itérateurs sur un objet concurrent_vector sont les mêmes que pour un objet vector.Les points suivants illustrent dans quelle mesure concurrent_vector diffère de vector :

  • Les opérations d'ajout, d'accès aux éléments, d'accès aux itérateurs et de parcourt des itérateurs sur un objet concurrent_vector sont sécurisées du point de vue de l'accès concurrentiel.

  • Vous pouvez ajouter des éléments uniquement à la fin d'un objet concurrent_vector.La classe concurrent_vector ne fournit pas la méthode insert.

  • Un objet concurrent_vector n'utilise pas la sémantique de déplacement lorsque vous le soumettez à un ajout.

  • La classe concurrent_vector ne fournit pas la méthode erase ou pop_back.Comme dans le cas de vector, utilisez la méthode clear pour supprimer tous les éléments d'un objet concurrent_vector.

  • La classe concurrent_vector ne stocke pas ses éléments en mémoire de façon contiguë.Par conséquent, vous ne pouvez pas utiliser la classe concurrent_vector de toutes les manières que vous pouvez utiliser un tableau.Par exemple, pour une variable nommée v de type concurrent_vector, l'expression &v[0]+2 produit un comportement indéfini.

  • La classe concurrent_vector définit les méthodes grow_by et grow_to_at_least.Ces méthodes ressemblent à la méthode resize, mais elles sont sécurisées du point de vue de l'accès concurrentiel.

  • Un objet concurrent_vector ne déplace pas ses éléments lorsque vous le redimensionnez ou vous le soumettez à un ajout.Cela permet aux pointeurs et aux itérateurs existants de rester valides pendant les opérations simultanées.

  • Le runtime ne définit pas de version spécialisée de concurrent_vector pour le type bool.

Dd504906.collapse_all(fr-fr,VS.110).gifOpérations à accès concurrentiel sécurisé

Toutes les méthodes qui ajoutent à ou augmentent la taille d'un objet concurrent_vector, ou accède à un élément dans un objet concurrent_vector, sont sécurisées du point de vue de l'accès concurrentiel.L'exception à cette règle est la méthode resize.

Le tableau suivant répertorie les méthodes et les opérateurs concurrent_vector courants qui sont sécurisées du point de vue de l'accès concurrentiel.

at

end

operator[]

begin

front

push_back

back

grow_by

rbegin

capacity

grow_to_at_least

rend

empty

max_size

taille

Les opérations que le runtime fournit pour la compatibilité avec la STL, par exemple, reserve, ne sont pas sécurisées d'accès concurrentiel.Le tableau suivant répertorie les méthodes et opérateurs courants qui ne sont pas sécurisées du point de vue de l'accès concurrentiel.

assign

reserve

clear

resize

operator=

shrink_to_fit

Les opérations qui modifient la valeur d'éléments existants ne sont pas sécurisées du point de vue de l'accès concurrentiel.Utilisez un objet de synchronisation, tel qu'un objet reader_writer_lock, pour synchroniser des opérations en lecture et en écriture simultanées au même élément de données.Pour plus d'informations sur les objets de synchronisation, consultez Structures de données de synchronisation.

Lorsque vous convertissez du code existant qui utilise vector de façon à utiliser concurrent_vector, les opérations simultanées peuvent provoquer un changement de comportement de votre application.Prenons l'exemple du programme suivant, qui effectue simultanément deux tâches sur un objet concurrent_vector.La première tâche ajoute des éléments supplémentaires à un objet concurrent_vector.La deuxième tâche calcule la somme de tous les éléments dans le même objet.

// 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;
      }
   );
}

Bien que la méthode end soit sécurisée du point de vue de l'accès concurrentiel, un appel simultané à la méthode push_back provoque une modification de la valeur retournée par end.Le nombre d'éléments parcourus par l'itérateur est indéterminé.Par conséquent, ce programme peut produire un résultat différent à chaque fois que vous l'exécutez.

Dd504906.collapse_all(fr-fr,VS.110).gifSécurité des exceptions

Si une opération d'assignation ou de croissance lève une exception, l'état de l'objet concurrent_vector devient non valide.Le comportement d'un objet concurrent_vector qui est dans un état non valide est indéfini, sauf indication contraire.Néanmoins, le destructeur libère toujours la mémoire allouée par l'objet, même si l'objet est dans un état non valide.

Le type de données des éléments vectoriels, _Ty, doit satisfaire les spécifications suivantes.Sinon, le comportement de la classe concurrent_vector est indéfini.

  • Le destructeur ne doit pas lever.

  • Si le constructeur de copie ou par défaut lève, le destructeur ne doit pas être déclaré à l'aide du mot clé virtual et il doit fonctionner correctement avec une mémoire initialisée à zéro.

Top

Classe concurrent_queue

Le concurrency::concurrent_queue de classe, tout comme le std::queue de classes, vous permet d'accéder à l'avant et arrière des éléments.La classe concurrent_queue permet d'effectuer des opérations de mise en file d'attente et de sortie de file d'attente sécurisées du point de vue de l'accès concurrentiel.La classe concurrent_queue fournit également une prise en charge des itérateurs qui n'est pas sécurisée du point de vue de l'accès concurrentiel.

Dd504906.collapse_all(fr-fr,VS.110).gifDifférences entre concurrent_queue et queue

La classe concurrent_queue ressemble étroitement à la classe queue.Les points suivants illustrent dans quelle mesure concurrent_queue diffère de queue :

  • Les opérations de mise en file d'attente et de sortie de file d'attente sur un objet concurrent_queue sont sécurisées du point de vue de l'accès concurrentiel.

  • La classe concurrent_queue fournit une prise en charge des itérateurs qui n'est pas sécurisée du point de vue de l'accès concurrentiel.

  • La classe concurrent_queue ne fournit pas la méthode front ou pop.La classe concurrent_queue remplace ces méthodes par la définition de la méthode try_pop.

  • La classe concurrent_queue ne fournit pas la méthode back.Par conséquent, vous ne pouvez pas faire référence à la fin de la file d'attente.

  • La classe concurrent_queue fournit la méthode unsafe_size au lieu de la méthode size.La méthode unsafe_size n'est pas sécurisée du point de vue de l'accès concurrentiel.

Dd504906.collapse_all(fr-fr,VS.110).gifOpérations à accès concurrentiel sécurisé

Toutes les méthodes qui mettent en file d'attente ou sortent de file d'attente dans ou à partir d'un objet concurrent_queue sont sécurisées du point de vue de l'accès concurrentiel.

Le tableau suivant répertorie les méthodes et les opérateurs concurrent_queue courants qui sont sécurisées du point de vue de l'accès concurrentiel.

empty

push

get_allocator

try_pop

Bien que la méthode empty soit sécurisée du point de vue de l'accès concurrentiel, une opération simultanée peut provoquer la croissance ou la réduction de la file d'attente avant le retour de la méthode empty.

Le tableau suivant répertorie les méthodes et opérateurs courants qui ne sont pas sécurisées du point de vue de l'accès concurrentiel.

clear

unsafe_end

unsafe_begin

unsafe_size

Dd504906.collapse_all(fr-fr,VS.110).gifPrise en charge des itérateurs

concurrent_queue fournit des itérateurs qui ne sont pas sécurisés du point de vue de l'accès concurrentiel.Nous vous recommandons d'utiliser ces itérateurs uniquement à des fins de débogage.

Un itérateur concurrent_queue parcourt des éléments uniquement dans le sens avant.Le tableau suivant répertorie les opérateurs pris en charge par chaque itérateur.

Opérateur

Description

operator++

Avance jusqu'au prochain élément dans la file d'attente.Cet opérateur est surchargé pour fournir une sémantique pré-incrément et post-incrément.

operator*

Extrait une référence à l'élément actuel.

operator->

Extrait un pointeur vers l'élément actuel.

Top

Classe concurrent_unordered_map

Le concurrency::concurrent_unordered_map est une classe de conteneur associatif qui, tout comme le std::unordered_map de classes, contrôles d'une séquence de longueur variable d'éléments de type std::pair < clé const, Ty >.Considérez une table non triée comme un dictionnaire que vous pouvez ajouter une paire clé / valeur à ou rechercher une valeur par clé.Cette classe est utile lorsque vous disposez de plusieurs threads ou des tâches qui doivent accéder à un conteneur partagé simultanément, il y insérer ou mettre à jour.

L'exemple suivant montre la structure de base pour l'utilisation de concurrent_unordered_map.Cet exemple insère les touches de caractères dans la plage [« a », ' i'].Parce que l'ordre des opérations est indéterminé, la valeur finale pour chacune des clés est également indéterminée.Toutefois, il est sûr effectuer les insertions en parallèle.

// 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]
*/

Pour obtenir un exemple qui utilise concurrent_unordered_map pour effectuer un mappage et réduire l'opération en parallèle, voir Comment : exécuter des opérations de mappage et de réduction en parallèle.

Dd504906.collapse_all(fr-fr,VS.110).gifDifférences entre concurrent_unordered_map et les unordered_map

La classe concurrent_unordered_map ressemble étroitement à la classe unordered_map.Les points suivants illustrent dans quelle mesure concurrent_unordered_map diffère de 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.Le unsafe_ convention d'affectation de noms indique que ces méthodes ne sont pas sécurisées d'accès concurrentiel.Pour plus d'informations sur la sécurité d'accès concurrentiel, consultez Opérations de concurrence d'accès sécurisé.

  • Opérations d'insertion n'invalide pas existants des pointeurs ou des itérateurs, ni faire, ils modifient l'ordre des éléments qui existent déjà dans le mappage.Insérer et de parcourir les opérations peuvent se produire simultanément.

  • concurrent_unordered_mapprend en charge transférer itération uniquement.

  • Insertion ne pas invalider ou mettre à jour les itérateurs sont retournées par equal_range.Insertion peut ajouter des éléments inégales à la fin de la plage.L'itérateur begin pointe vers un élément égal.

Afin d'éviter un blocage, aucune méthode de concurrent_unordered_map détient un verrou lorsqu'il appelle l'allocateur de mémoire, les fonctions de hachage ou tout autre code défini par l'utilisateur.En outre, vous devez vous assurer que la fonction de hachage évalue toujours les clés égales à la même valeur.Les meilleures fonctions de hachage distribuent uniformément les clés dans l'espace de code de hachage.

Dd504906.collapse_all(fr-fr,VS.110).gifOpérations à accès concurrentiel sécurisé

Le concurrent_unordered_map classe permet les opérations d'insertion et accès à l'élément de concurrence d'accès sécurisé.Opérations d'insertion n'invalide pas existants des pointeurs ou des itérateurs.Les opérations de parcourt et d'accès aux itérateurs sont également sécurisées du point de vue de l'accès concurrentiel.Le tableau suivant montre les communément utilisées concurrent_unordered_map méthodes et les opérateurs qui sont sécurisés au niveau d'accès concurrentiel.

at

count

find

key_eq

begin

empty

get_allocator

max_size

cbegin

end

hash_function

operator[]

cend

equal_range

Insérer

size

Bien que le count méthode peut être appelée en toute sécurité à partir de threads en cours d'exécution simultanément, des threads différents peuvent recevoir des résultats différents si une nouvelle valeur est simultanément insérée dans le conteneur.

Le tableau suivant illustre les méthodes couramment utilisées et les opérateurs qui ne sont pas sécurisées d'accès concurrentiel.

clear

max_load_factor

rehash

load_factor

operator=

swap

En plus de ces méthodes, toute méthode qui commence par unsafe_ est également pas de concurrence d'accès sécurisé.

Top

Classe concurrent_unordered_multimap

Le concurrency::concurrent_unordered_multimap classe ressemble étroitement à la concurrent_unordered_map sauf qu'elle permet à plusieurs valeurs à mapper à la même clé de classe.Elle diffère également de concurrent_unordered_map dans l'une des manières suivantes :

  • Le concurrent_unordered_multimap::insert méthode retourne un itérateur au lieu de std::pair<iterator, bool>.

  • Le concurrent_unordered_multimap classe ne fournit pas operator[] , ni le at méthode.

L'exemple suivant montre la structure de base pour l'utilisation de concurrent_unordered_multimap.Cet exemple insère les touches de caractères dans la plage [« a », ' i'].concurrent_unordered_multimappermet à une clé à valeurs multiples.

// 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

Classe concurrent_unordered_set

Le concurrency::concurrent_unordered_set classe ressemble étroitement à la concurrent_unordered_map classe sauf qu'il gère les valeurs au lieu de paires clé / valeur.Le concurrent_unordered_set classe ne fournit pas operator[] , ni le at méthode.

L'exemple suivant montre la structure de base pour l'utilisation de concurrent_unordered_set.Cet exemple insère les valeurs de caractères dans la plage [« a », ' i'].Il est recommandé d'effectuer les insertions en parallèle.

// 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

Classe concurrent_unordered_multiset

Le concurrency::concurrent_unordered_multiset classe ressemble étroitement à la concurrent_unordered_set classe sauf qu'elle autorise les valeurs en double.Elle diffère également de concurrent_unordered_set dans l'une des manières suivantes :

  • Le concurrent_unordered_multiset::insert méthode retourne un itérateur au lieu de std::pair<iterator, bool>.

  • Le concurrent_unordered_multiset classe ne fournit pas operator[] , ni le at méthode.

L'exemple suivant montre la structure de base pour l'utilisation de concurrent_unordered_multiset.Cet exemple insère les valeurs de caractères dans la plage [« a », ' i'].concurrent_unordered_multisetpermet à une valeur de se produire plusieurs fois.

// 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

Classe combinable

Le concurrency::combinable classe fournit le stockage local des threads réutilisable qui vous permet d'effectuer des calculs précis et puis fusionner ces calculs dans un résultat final.On peut considérer un objet combinable comme une variable de réduction.

La classe combinable est utile lorsque vous avez une ressource partagée par plusieurs threads ou tâches.La classe combinable vous aide à éliminer l'état partagé en fournissant l'accès aux ressources partagées sans verrou.Par conséquent, cette classe procure une alternative à l'utilisation d'un mécanisme de synchronisation, par exemple un mutex, pour synchroniser l'accès aux données partagées à partir de plusieurs threads.

Dd504906.collapse_all(fr-fr,VS.110).gifMéthodes et fonctionnalités

Le tableau suivant montre quelques-unes des méthodes importantes de la classe combinable.Pour plus d'informations sur toutes les méthodes de la classe combinable, consultez combinable, classe.

Méthode

Description

locales

Extrait une référence à la variable locale associée au contexte de thread actuel.

clear

Supprime toutes les variables de thread local de l'objet combinable.

combine

combine_each

Utilise la fonction combine fournie pour générer une valeur finale à partir de l'ensemble de tous les calculs de thread locaux.

La classe combinable est une classe de modèle paramétrable sur le résultat fusionné final.Si vous appelez le constructeur par défaut, le type de paramètre de modèle _Ty doit avoir un constructeur par défaut et un constructeur de copie.Si le type de paramètre de modèle _Ty n'a pas de constructeur par défaut, appelez la version surchargée du constructeur qui prend une fonction d'initialisation comme paramètre.

Vous pouvez stocker des données supplémentaires dans un objet combinable après avoir appelé la méthode combine ou combine_each.Vous pouvez également appeler les méthodes combine et combine_each à plusieurs reprises.Si aucune valeur locale dans un objet combinable ne change, les méthodes combine_each et combine produisent le même résultat chaque fois qu'elles sont appelées.

Dd504906.collapse_all(fr-fr,VS.110).gifExemples

Pour obtenir des exemples d'utilisation de la classe combinable, consultez les rubriques suivantes :

Top

Rubriques connexes

Référence

Classe concurrent_vector

concurrent_queue, classe

concurrent_unordered_map, classe

concurrent_unordered_multimap, classe

concurrent_unordered_set, classe

concurrent_unordered_multiset, classe

combinable, classe