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.
Diffé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.
Opé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.
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.
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.
Sé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.
Diffé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.
Opé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.
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.
Prise 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 |
---|---|
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. |
|
Extrait une référence à l'élément actuel. |
|
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.
Diffé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.
Opé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.
count |
find |
||
begin |
empty |
get_allocator |
max_size |
cbegin |
end |
hash_function |
|
cend |
equal_range |
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 |
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.
Mé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 |
---|---|
Extrait une référence à la variable locale associée au contexte de thread actuel. |
|
Supprime toutes les variables de thread local de l'objet combinable. |
|
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.
Exemples
Pour obtenir des exemples d'utilisation de la classe combinable, consultez les rubriques suivantes :
Comment : utiliser la classe combinable pour améliorer les performances
Comment : utiliser la classe combinable pour combiner des ensembles
Top
Rubriques connexes
Comment : utiliser des conteneurs parallèles pour une efficacité accrue
Indique comment utiliser des conteneurs parallèles pour stocker et accéder efficacement à des données en parallèle.Comment : utiliser la classe combinable pour améliorer les performances
Indique comment utiliser la classe combinable pour éliminer l'état partagé, et ainsi améliorer les performances.Comment : utiliser la classe combinable pour combiner des ensembles
Indique comment utiliser une fonction combine pour fusionner des groupes de données de threads locaux.Bibliothèque de modèles parallèles
Décrit la bibliothèque PPL, qui fournit un modèle de programmation impérative qui encourage l'extensibilité et la facilité d'utilisation pour le développement d'applications simultanées.
Référence
concurrent_unordered_map, classe
concurrent_unordered_multimap, classe
concurrent_unordered_set, classe