Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
De PPL (Parallel Patterns Library) bevat verschillende containers en objecten die thread-veilige toegang tot hun elementen bieden.
Een gelijktijdige container biedt gelijktijdigheidsveilige toegang tot de belangrijkste bewerkingen. Hier zijn gelijktijdigheidsveilige aanwijzers of iterators altijd geldig. Het is geen garantie voor het initialiseren van elementen of van een bepaalde doorkruisingsvolgorde. De functionaliteit van deze containers lijkt op de containers die worden geleverd door de C++-standaardbibliotheek. De klasse gelijktijdigheid::concurrent_vector lijkt bijvoorbeeld op de klasse std::vector , behalve dat u met de concurrent_vector klasse elementen parallel kunt toevoegen. Gebruik gelijktijdige containers wanneer u parallelle code hebt waarvoor zowel lees- als schrijftoegang tot dezelfde container is vereist.
Een gelijktijdig object wordt gelijktijdig gedeeld tussen onderdelen. Een proces waarmee de status van een gelijktijdig object parallel wordt berekend, levert hetzelfde resultaat op als een ander proces waarmee dezelfde status serieel wordt berekend. De gelijktijdigheid::combinatieklasse is één voorbeeld van een gelijktijdig objecttype. Met de combinable klasse kunt u berekeningen parallel uitvoeren en deze berekeningen vervolgens combineren tot een eindresultaat. Gebruik gelijktijdige objecten wanneer u anders een synchronisatiemechanisme gebruikt, bijvoorbeeld een mutex, om de toegang tot een gedeelde variabele of resource te synchroniseren.
Afdelingen
In dit onderwerp worden de volgende parallelle containers en objecten gedetailleerd beschreven.
Gelijktijdige containers:
Gelijktijdige objecten:
concurrent_vector-klasse
De gelijktijdigheid::concurrent_vector klasse is een reekscontainerklasse waarmee u, net als de klasse std::vector , willekeurig toegang hebt tot de elementen. De concurrent_vector klasse maakt gelijktijdigheidsveilige bewerkingen voor toevoegen en elementtoegang mogelijk. Toevoegbewerkingen maken bestaande aanwijzers of iterators niet ongeldig. Iterator toegang- en doorloopbewerkingen zijn ook gelijktijdigheidsveilig. Hier zijn gelijktijdigheidsveilige aanwijzers of iterators altijd geldig. Het is geen garantie voor het initialiseren van elementen of van een bepaalde doorkruisingsvolgorde.
Verschillen tussen concurrent_vector en vector
De concurrent_vector klasse lijkt sterk op de vector klasse. De complexiteit van toevoeg-, elementtoegangs- en iterator-toegangsbewerkingen op een concurrent_vector object zijn hetzelfde als voor een vector object. De volgende punten laten zien waar concurrent_vector het verschil is tussen vector:
Toevoeg-, elementtoegang, iteratortoegang en iterator-traversal-bewerkingen voor een
concurrent_vectorobject zijn gelijktijdigheidsveilig.U kunt alleen elementen toevoegen aan het einde van een
concurrent_vectorobject. Deconcurrent_vectorklasse biedt deinsertmethode niet.Een
concurrent_vectorobject gebruikt geen verplaatsingssemantiek wanneer u eraan toevoegt.De
concurrent_vectorklasse biedt deeraseofpop_backmethoden niet. Net als bijvector, gebruikt u de duidelijke methode om alle elementen uit eenconcurrent_vectorobject te verwijderen.De
concurrent_vectorklasse slaat de elementen niet aaneengesloten op in het geheugen. Daarom kunt u deconcurrent_vectorklasse niet op alle manieren gebruiken waarop u een matrix kunt gebruiken. Voor een variabele met de naamvvan het typeconcurrent_vectorproduceert de expressie&v[0]+2bijvoorbeeld niet-gedefinieerd gedrag.De
concurrent_vectorklasse definieert de methoden grow_by en grow_to_at_least . Deze methoden lijken op de methode voor het wijzigen van de grootte , behalve dat ze gelijktijdigheidsveilig zijn.Een
concurrent_vectorobject verplaatst de elementen niet wanneer u eraan toevoegt of het formaat ervan wijzigt. Hierdoor kunnen bestaande aanwijzers en iterators geldig blijven tijdens gelijktijdige bewerkingen.De runtime definieert geen gespecialiseerde versie van
concurrent_vectorvoor het typebool.
Operaties voor Concurrency-Safe
Alle methoden die worden toegevoegd aan of de grootte van een concurrent_vector object vergroten of toegang krijgen tot een element in een concurrent_vector object, zijn gelijktijdigheidsveilig. Hier zijn gelijktijdigheidsveilige aanwijzers of iterators altijd geldig. Het is geen garantie voor het initialiseren van elementen of van een bepaalde doorkruisingsvolgorde. De uitzondering op deze regel is de resize methode.
In de volgende tabel ziet u de algemene concurrent_vector methoden en operators die gelijktijdigheidsveilig zijn.
Bewerkingen die de runtime biedt voor compatibiliteit met de C++ Standard-bibliotheek, bijvoorbeeld, reservezijn niet gelijktijdigheidsveilig. In de volgende tabel ziet u de algemene methoden en operators die niet gelijktijdigheidsveilig zijn.
Bewerkingen die de waarde van bestaande elementen wijzigen, zijn niet gelijktijdigheidsveilig. Gebruik een synchronisatieobject zoals een reader_writer_lock-object om gelijktijdige lees- en schrijfbewerkingen te synchroniseren met hetzelfde gegevenselement. Zie Synchronisatiegegevensstructuren voor meer informatie over synchronisatieobjecten.
Wanneer u bestaande code converteert die vector gebruikt om concurrent_vector te gebruiken, kunnen gelijktijdige bewerkingen ervoor zorgen dat het gedrag van uw toepassing verandert. Denk bijvoorbeeld aan het volgende programma waarmee gelijktijdig twee taken op een concurrent_vector object worden uitgevoerd. Met de eerste taak worden extra elementen aan een concurrent_vector object toegevoegd. De tweede taak berekent de som van alle elementen in hetzelfde object.
// 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;
}
);
}
Hoewel de end methode gelijktijdigheidsveilig is, veroorzaakt een gelijktijdige aanroep van de methode push_back de waarde die wordt geretourneerd door end te wijzigen. Het aantal elementen dat de iterator doorkruist, is onbepaald. Daarom kan dit programma elke keer dat u het uitvoert een ander resultaat produceren. Wanneer het elementtype niet triviaal is, is het mogelijk dat er een racevoorwaarde bestaat tussen push_back en end aanroepen. De end-methode kan een gealloceerd element retourneren, maar niet volledig geïnitialiseerd.
Uitzonderingsveiligheid
Als een groei- of toewijzingsbewerking een uitzondering genereert, wordt de status van het concurrent_vector object ongeldig. Het gedrag van een concurrent_vector object dat een ongeldige status heeft, is niet gedefinieerd, tenzij anders wordt vermeld. De destructor maakt echter altijd het geheugen vrij dat het object toewijst, zelfs als het object zich in een ongeldige toestand bevindt.
Het gegevenstype van de vectorelementen Tmoet voldoen aan de volgende vereisten. Anders is het gedrag van de concurrent_vector klasse niet gedefinieerd.
De destructor mag niet gooien.
Als de standaard- of kopieerconstructor een uitzondering veroorzaakt, mag de destructor niet worden gedeclareerd met behulp van het
virtualtrefwoord en moet deze correct werken met null-geïnitialiseerd geheugen.
[Boven]
concurrent_queue-klasse
De concurrency::concurrent_queue-klasse, net als de std::queue-klasse, biedt toegang tot de elementen aan de voorkant en de achterkant. De concurrent_queue klasse maakt gelijktijdigheidsveilige enqueue- en dequeue-bewerkingen mogelijk. Hier zijn gelijktijdigheidsveilige aanwijzers of iterators altijd geldig. Het is geen garantie voor het initialiseren van elementen of van een bepaalde doorkruisingsvolgorde. De concurrent_queue klasse biedt ook iterator-ondersteuning die niet gelijktijdigheidsveilig is.
Verschillen tussen concurrent_queue en wachtrij
De concurrent_queue klasse lijkt sterk op de queue klasse. De volgende punten laten zien waar concurrent_queue het verschil is tussen queue:
Bewerkingen voor het toevoegen aan een
concurrent_queueobject in de wachtrij en het verwijderen uit de wachtrij zijn gelijktijdigheidsveilig.De
concurrent_queueklasse biedt iterator-ondersteuning die niet gelijktijdigheidsveilig is.De
concurrent_queueklasse biedt defrontofpopmethoden niet. Deconcurrent_queueklasse vervangt deze methoden door de methode try_pop te definiëren.De
concurrent_queueklasse biedt debackmethode niet. Daarom kunt u niet verwijzen naar het einde van de wachtrij.De
concurrent_queueklasse biedt de unsafe_size methode in plaats van desizemethode. Deunsafe_sizemethode is niet gelijktijdigheidsveilig.
Operaties voor Concurrency-Safe
Alle methoden die naar of uit een concurrent_queue object enqueuen of dequeuen, zijn gelijktijdigheidsveilig. Hier zijn gelijktijdigheidsveilige aanwijzers of iterators altijd geldig. Het is geen garantie voor het initialiseren van elementen of van een bepaalde doorkruisingsvolgorde.
In de volgende tabel ziet u de algemene concurrent_queue methoden en operators die gelijktijdigheidsveilig zijn.
Hoewel de empty methode gelijktijdigheidsveilig is, kan een gelijktijdige bewerking ertoe leiden dat de wachtrij toeneemt of verkleint voordat de empty methode wordt geretourneerd.
In de volgende tabel ziet u de algemene methoden en operators die niet gelijktijdigheidsveilig zijn.
Ondersteuning voor iterator
De concurrent_queue biedt iterators die niet gelijktijdigheidsveilig zijn. U wordt aangeraden deze iterators alleen te gebruiken voor foutopsporing.
Een concurrent_queue iterator doorkruist alleen elementen in de voorwaartse richting. In de volgende tabel ziet u de operators die door elke iterator worden ondersteund.
| Operateur | Beschrijving |
|---|---|
operator++ |
Hiermee gaat u naar het volgende item in de wachtrij. Deze operator is overbeladen om zowel pre-increment- als post-incrementsemantiek te bieden. |
operator* |
Hiermee wordt een verwijzing naar het huidige item opgehaald. |
operator-> |
Hiermee wordt een aanwijzer naar het huidige item opgehaald. |
[Boven]
concurrent_unordered_map-klasse
De concurrency::concurrent_unordered_map-klasse is een associatieve containerklasse die, net als de std::unordered_map-klasse, een reeks van variërende lengte van elementen van het type std::pair<const Key, Ty> beheert. U kunt een niet-geordende kaart beschouwen als een woordenlijst waarmee u een sleutel- en waardepaar kunt toevoegen of een waarde per sleutel kunt opzoeken. Deze klasse is handig wanneer u meerdere threads of taken hebt die gelijktijdig toegang moeten hebben tot een gedeelde container, erin moeten invoegen of bijwerken.
In het volgende voorbeeld ziet u de basisstructuur voor het gebruik van concurrent_unordered_map. In dit voorbeeld worden tekentoetsen ingevoegd in het bereik ['a', 'i']. Omdat de volgorde van bewerkingen onbepaald is, is de uiteindelijke waarde voor elke sleutel ook niet bepaald. Het is echter veilig om de invoegingen parallel uit te voeren.
// 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]
*/
Voor een voorbeeld dat gebruik maakt van concurrent_unordered_map om toewijzings- en reductiebewerkingen parallel uit te voeren, zie Instructies voor het parallel uitvoeren van toewijzings- en reductiebewerkingen.
Verschillen tussen concurrent_unordered_map en unordered_map
De concurrent_unordered_map klasse lijkt sterk op de unordered_map klasse. De volgende punten laten zien waar concurrent_unordered_map het verschil is tussen unordered_map:
De
erasemethoden ,bucketenbucket_countbucket_sizemethoden hebben respectievelijk de naamunsafe_erase,unsafe_bucket,unsafe_bucket_countenunsafe_bucket_size, . Deunsafe_naamconventie geeft aan dat deze methoden niet gelijktijdigheidsveilig zijn. Zie Concurrency-Safe Bewerkingen voor meer informatie over gelijktijdigheidsveiligheid.Invoegbewerkingen maken bestaande aanwijzers of iterators niet ongeldig, noch wijzigen ze de volgorde van items die al in de kaart aanwezig zijn. Invoeg- en traversebewerkingen kunnen gelijktijdig plaatsvinden.
concurrent_unordered_mapondersteunt alleen voorwaartse iteratie.De invoeging maakt de iterators die worden geretourneerd door
equal_rangeniet ongeldig en werkt ze ook niet bij. Het invoegen kan ongelijksoortige items aan het einde van het bereik toevoegen. De begin-iterator verwijst naar een gelijk item.
Om impasses te voorkomen, houdt geen enkele methode van concurrent_unordered_map een vergrendeling vast wanneer het de geheugentoewijzer, hashfuncties of andere door de gebruiker gedefinieerde code aanroept. U moet er ook voor zorgen dat de hash-functie altijd gelijke sleutels evalueert op dezelfde waarde. De beste hashfuncties verdelen sleutels uniform over de hash-coderuimte.
Operaties voor Concurrency-Safe
De concurrent_unordered_map klasse maakt gelijktijdigheidsveilige invoeg- en elementtoegangsbewerkingen mogelijk. Invoegbewerkingen maken bestaande aanwijzers of iterators niet ongeldig. Iterator toegang- en doorloopbewerkingen zijn ook gelijktijdigheidsveilig. Hier zijn gelijktijdigheidsveilige aanwijzers of iterators altijd geldig. Het is geen garantie voor het initialiseren van elementen of van een bepaalde doorkruisingsvolgorde. In de volgende tabel ziet u de veelgebruikte concurrent_unordered_map methoden en operators die gelijktijdigheidsveilig zijn.
Hoewel de count methode veilig kan worden aangeroepen vanuit gelijktijdig actieve threads, kunnen verschillende threads verschillende resultaten ontvangen als er tegelijkertijd een nieuwe waarde wordt ingevoegd in de container.
In de volgende tabel ziet u de veelgebruikte methoden en operators die niet gelijktijdigheidsveilig zijn.
Naast deze methoden is elke methode waarmee wordt begonnen unsafe_ ook niet gelijktijdigheidsveilig.
[Boven]
concurrent_unordered_multimap-klasse
De klasse concurrency::concurrent_unordered_multimap lijkt sterk op de concurrent_unordered_map klasse, maar staat toe dat meerdere waarden aan dezelfde sleutel worden toegewezen. Het verschilt van concurrent_unordered_map op de volgende manieren:
De methode concurrent_unordered_multimap::insert retourneert een iterator in plaats van
std::pair<iterator, bool>.De
concurrent_unordered_multimapklasse biedtoperator[]niet en deatmethode niet.
In het volgende voorbeeld ziet u de basisstructuur voor het gebruik van concurrent_unordered_multimap. In dit voorbeeld worden tekentoetsen ingevoegd in het bereik ['a', 'i'].
concurrent_unordered_multimap hiermee kan een sleutel meerdere waarden hebben.
// 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]
*/
[Boven]
concurrent_unordered_set-klasse
De gelijktijdigheid::concurrent_unordered_set klasse lijkt sterk op de concurrent_unordered_map klasse, behalve dat deze waarden beheert in plaats van sleutel- en waardeparen. De concurrent_unordered_set klasse biedt operator[] niet en de at methode niet.
In het volgende voorbeeld ziet u de basisstructuur voor het gebruik van concurrent_unordered_set. In dit voorbeeld worden tekenwaarden ingevoegd in het bereik ['a', 'i']. Het is veilig om de invoegingen parallel uit te voeren.
// 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]
*/
[Boven]
concurrent_unordered_multiset-klasse
De gelijktijdigheid::concurrent_unordered_multiset klasse lijkt sterk op de concurrent_unordered_set klasse, behalve dat het dubbele waarden toestaat. Het verschilt van concurrent_unordered_set op de volgende manieren:
De methode concurrent_unordered_multiset::insert retourneert een iterator in plaats van
std::pair<iterator, bool>.De
concurrent_unordered_multisetklasse biedtoperator[]niet en deatmethode niet.
In het volgende voorbeeld ziet u de basisstructuur voor het gebruik van concurrent_unordered_multiset. In dit voorbeeld worden tekenwaarden ingevoegd in het bereik ['a', 'i'].
concurrent_unordered_multiset hiermee kan een waarde meerdere keren optreden.
// 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]
*/
[Boven]
combineerbare klasse
De gelijktijdigheid::combineerbare klasse biedt herbruikbare, thread-lokale opslag waarmee u gedetailleerde berekeningen kunt uitvoeren en deze berekeningen vervolgens kunt samenvoegen in een eindresultaat. U kunt een combinable object beschouwen als een reductievariabele.
De combinable klasse is handig wanneer u een resource hebt die wordt gedeeld tussen verschillende threads of taken. De combinable klasse helpt u gedeelde status te elimineren door op een vergrendelingsvrije manier toegang te bieden tot gedeelde resources. Daarom biedt deze klasse een alternatief voor het gebruik van een synchronisatiemechanisme, bijvoorbeeld een mutex, om de toegang tot gedeelde gegevens vanuit meerdere threads te synchroniseren.
Methoden en functies
In de volgende tabel ziet u enkele belangrijke methoden van de combinable klasse. Zie combinable voor meer informatie over alle klassemethoden.
| Methode | Beschrijving |
|---|---|
| lokale | Hiermee wordt een verwijzing opgehaald naar de lokale variabele die is gekoppeld aan de huidige threadcontext. |
| wissen | Hiermee verwijdert u alle thread-lokale variabelen uit het combinable object. |
|
combineren combine_each |
Maakt gebruik van de opgegeven combinatiefunctie om een uiteindelijke waarde te genereren op basis van de set met alle thread-lokale berekeningen. |
De combinable klasse is een sjabloonklasse die wordt geparameteriseerd op het uiteindelijke samengevoegde resultaat. Als u de standaardconstructor aanroept, moet het parametertype van de T sjabloon een standaardconstructor en een kopieerconstructor hebben. Als het parametertype van de T sjabloon geen standaardconstructor heeft, roept u de overbelaste versie van de constructor aan die een initialisatiefunctie als parameter gebruikt.
U kunt extra gegevens opslaan in een combinable object nadat u de methoden combineren of combine_each hebt aangeroepen. U kunt de combine en combine_each methoden ook meerdere keren aanroepen. Als er geen lokale waarde in een combinable object verandert, produceren de combine en combine_each methoden hetzelfde resultaat telkens wanneer ze worden aangeroepen.
Voorbeelden
Zie de volgende onderwerpen voor voorbeelden over het gebruik van de combinable klasse:
[Boven]
Verwante onderwerpen
Procedure: Parallelle containers gebruiken om de efficiëntie te verhogen
Laat zien hoe u parallelle containers gebruikt om gegevens efficiënt op te slaan en te openen.
Procedure: Combineerbaar gebruiken om de prestaties te verbeteren
Laat zien hoe u de klasse gebruikt om de combinable gedeelde status te elimineren en zo de prestaties te verbeteren.
Hoe: Combinerend gebruiken om sets te combineren
Laat zien hoe u een combine functie gebruikt om thread-lokale gegevenssets samen te voegen.
Bibliotheek met parallelle patronen (PPL)
Beschrijft de PPL, die een imperatief programmeermodel biedt dat schaalbaarheid en gebruiksgemak bevordert voor het ontwikkelen van gelijktijdige toepassingen.
Referentie
concurrent_unordered_map-klasse
concurrent_unordered_multimap-klasse
concurrent_unordered_set-klasse