Parallele Container und Objekte
Die Parallel Patterns Library (PPL) enthält mehrere Container und Objekte, die threadsicheren Zugriff auf ihre Elemente bieten.
Parallele Container bieten parallelitätssicheren Zugriff auf die wichtigsten Vorgänge.Die Funktionalität dieser Container ist mit denen der Standardvorlagenbibliothek (Standard Template Library, STL) vergleichbar.Beispielsweise die concurrency::concurrent_vector Klasse ähnelt der Std:: Vector -Klasse, mit Ausnahme der concurrent_vector -Klasse können Sie die Elemente parallel anfügen.Verwenden Sie parallele Container, wenn Sie mit parallelem Code arbeiten, der Lese- und Schreibzugriff auf den gleichen Container erfordert.
Parallele Objekte werden von Komponenten gleichzeitig gemeinsam genutzt.Ein Prozess zur parallelen Berechnung des Zustands eines parallelen Objekts und ein Prozess zur seriellen Berechnung desselben Zustands führen zum gleichen Ergebnis.Die concurrency::combinable -Klasse ist ein Beispiel für eine gleichzeitige Objekttyp.Mit der combinable-Klasse können Sie Berechnungen parallel ausführen und diese dann in einem Endergebnis kombinieren.Verwenden Sie parallele Objekte, wo Sie andernfalls einen Synchronisierungsmechanismus wie z. B. einen Mutex einsetzen würden, um den Zugriff auf eine freigegebene Variable oder Ressource zu synchronisieren.
Abschnitte
In diesem Thema werden die folgenden parallelen Container und Objekte im Detail beschrieben.
Parallele Container:
concurrent_vector-Klasse
Unterschiede zwischen concurrent_vector und vector
Parallelitätssichere Vorgänge
Ausnahmesicherheit
concurrent_queue-Klasse
Unterschiede zwischen concurrent_queue und queue
Parallelitätssichere Vorgänge
Iteratorunterstützung
Concurrent_unordered_map-Klasse
Unterschiede zwischen Concurrent_unordered_map und unordered_map
Parallelitätssichere Vorgänge
Concurrent_unordered_multimap-Klasse
Concurrent_unordered_set-Klasse
Concurrent_unordered_multiset-Klasse
Parallele Objekte:
combinable-Klasse
Methoden und Funktionen
Beispiele
concurrent_vector-Klasse
Die concurrency::concurrent_vector ist eine Sequenz-Container-Klasse, die genau wie die Std:: Vector Klasse, können Sie nach dem Zufallsprinzip seine Elemente zugreifen.Die concurrent_vector-Klasse ermöglicht das parallelitätssichere Anfügen von und Zugreifen auf Elemente.Vorhandene Zeiger und Iteratoren bleiben bei Anfügevorgängen gültig.Iteratorzugriff und Durchlaufvorgänge sind ebenfalls parallelitätssicher.
Unterschiede zwischen concurrent_vector und vector
Die concurrent_vector-Klasse stimmt mit der vector-Klasse weitgehend überein.Anfügevorgänge, Elementzugriff und Iteratorzugriff sind für ein concurrent_vector-Objekt genauso komplex wie für ein vector-Objekt.Im Folgenden sind die Unterschiede zwischen concurrent_vector und vector aufgeführt:
Anfügevorgänge, Elementzugriff, Iteratorzugriff und Iteratordurchläufe sind bei einem concurrent_vector-Objekt parallelitätssicher.
Das Hinzufügen von Elementen ist nur am Ende eines concurrent_vector-Objekts möglich.Die concurrent_vector-Klasse stellt die insert-Methode nicht bereit.
Das Anfügen an ein concurrent_vector-Objekt erfolgt ohne Verschiebesemantik.
Die concurrent_vector-Klasse stellt weder die erase-Methode noch die pop_back-Methode bereit.Verwenden Sie wie bei vector die clear-Methode, wenn alle Elemente aus einem concurrent_vector-Objekt entfernt werden sollen.
Die Elemente der concurrent_vector-Klasse werden nicht zusammenhängend im Arbeitsspeicher gespeichert.Daher bietet die concurrent_vector-Klasse nicht alle Möglichkeiten eines Arrays.Für eine Variable mit dem Namen v vom Typ concurrent_vector ergibt der Ausdruck &v[0]+2 beispielsweise nicht definiertes Verhalten.
Die concurrent_vector-Klasse definiert die grow_by-Methode und die grow_to_at_least-Methode.Diese Methoden entsprechen der resize-Methode, sind aber parallelitätssicher.
Die Elemente eines concurrent_vector-Objekts werden nicht verschoben, wenn Sie Elemente daran anfügen oder die Größe ändern.So bleiben vorhandene Zeiger und Iteratoren bei gleichzeitigen Vorgängen gültig.
Zur Laufzeit wird keine spezialisierte Version von concurrent_vector für den bool-Typ definiert.
Parallelitätssichere Vorgänge
Alle Methoden zum Anfügen an ein concurrent_vector-Objekt, Vergrößern des Objekts oder Zugreifen auf ein Element in einem concurrent_vector-Objekt sind parallelitätssicher.Eine Ausnahme von dieser Regel ist die resize-Methode.
Die folgende Tabelle enthält alle gängigen parallelitätssicheren concurrent_vector-Methoden und -Operatoren.
Vorgänge, die die Common Language Runtime für die Kompatibilität mit der STL, beispielsweise bietet reserve, sind nicht sicher.Die folgende Tabelle enthält alle gängigen Methoden und Operatoren, die nicht parallelitätssicher sind.
Vorgänge, mit denen der Wert vorhandener Elemente geändert wird, sind nicht parallelitätssicher.Verwenden Sie ein Synchronisierungsobjekt, wie z. B. ein reader_writer_lock-Objekt, um gleichzeitige Lese- und Schreibvorgänge für das gleiche Datenelement zu synchronisieren.Weitere Informationen zu Synchronisierungsobjekten finden Sie unter Synchronisierungsdatenstrukturen.
Wenn Sie vorhandenen Code konvertieren, sodass dieser nicht mehr vector, sondern concurrent_vector verwendet, kann sich das Verhalten Ihrer Anwendung aufgrund von gleichzeitigen Vorgängen ändern.Betrachten Sie beispielsweise das folgende Programm, das gleichzeitig zwei Aufgaben für ein concurrent_vector-Objekt ausführt.Die erste Aufgabe fügt an ein concurrent_vector-Objekt zusätzliche Elemente an.Die zweite Aufgabe berechnet die Summe aller Elemente diesem Objekt.
// 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;
}
);
}
Die end-Methode ist zwar parallelitätssicher, aber ein gleichzeitiger Aufruf der push_back-Methode bewirkt, dass sich der von end zurückgegebene Wert ändert.Die Anzahl von Elementen, die der Iterator durchläuft, ist unbestimmt.Deshalb kann dieses Programm bei jeder Ausführung ein anderes Ergebnis liefern.
Ausnahmesicherheit
Im Falle einer Ausnahme bei einem Zuwachs- oder Zuweisungsvorgang wird der Zustand des concurrent_vector-Objekts ungültig.Das Verhalten eines concurrent_vector-Objekts mit ungültigem Zustand ist nicht definiert, falls nicht anders angegeben.Der vom Objekt zugewiesene Speicher wird jedoch vom Destruktor immer freigegeben, auch wenn der Zustand des Objekts ungültig ist.
Der Datentyp _Ty der Vektorelemente muss die folgenden Anforderungen erfüllen.Andernfalls ist das Verhalten der concurrent_vector-Klasse nicht definiert.
Der Destruktor darf nicht auslösen.
Wenn der Standard- oder Kopierkonstruktor auslöst, darf der Destruktor nicht mit dem virtual-Schlüsselwort deklariert werden und muss ordnungsgemäß mit nullinitialisiertem Arbeitsspeicher funktionieren.
Top
concurrent_queue-Klasse
Die concurrency::concurrent_queue -Klasse, wie die std::queue Klasse und ermöglicht Ihnen Zugriff auf die Vorderseite sowie Elemente zurück.Die concurrent_queue-Klasse ermöglicht parallelitätssichere enqueue- und dequeue-Vorgänge.Die concurrent_queue-Klasse bietet außerdem nicht parallelitätssichere Iteratorunterstützung.
Unterschiede zwischen concurrent_queue und queue
Die concurrent_queue-Klasse stimmt mit der queue-Klasse weitgehend überein.Im Folgenden sind die Unterschiede zwischen concurrent_queue und queue aufgeführt:
Enqueue- und dequeue-Vorgänge für ein concurrent_queue-Objekt sind parallelitätssicher.
Die concurrent_queue-Klasse bietet nicht parallelitätssichere Iteratorunterstützung.
Die concurrent_queue-Klasse stellt weder die front-Methode noch die pop-Methode bereit.Durch das Definieren der try_pop-Methode ersetzt die concurrent_queue-Klasse diese Methoden.
Die concurrent_queue-Klasse stellt die back-Methode nicht bereit.Daher können Sie nicht auf das Ende der Warteschlange verweisen.
Die concurrent_queue-Klasse stellt die unsafe_size-Methode statt der size-Methode bereit.Die unsafe_size-Methode ist nicht parallelitätssicher.
Parallelitätssichere Vorgänge
Alle Methoden für enqueue- und dequeue-Vorgänge eines concurrent_queue-Objekts sind parallelitätssicher.
Die folgende Tabelle enthält alle gängigen parallelitätssicheren concurrent_queue-Methoden und -Operatoren.
Obwohl die empty-Methode parallelitätssicher ist, kann ein gleichzeitiger Vorgang zur Folge haben, dass die Warteschlange vergrößert oder verkleinert wird, bevor die empty-Methode einen Wert zurückgibt.
Die folgende Tabelle enthält alle gängigen Methoden und Operatoren, die nicht parallelitätssicher sind.
Iteratorunterstützung
Die concurrent_queue-Klasse stellt Iteratoren bereit, die nicht parallelitätssicher sind.Diese Iteratoren sollten nur zum Debuggen verwendet werden.
Ein concurrent_queue-Iterator durchläuft Elemente nur in Vorwärtsrichtung.Die folgende Tabelle enthält die von den einzelnen Iteratoren unterstützten Operatoren.
Operator |
Beschreibung |
---|---|
Wechselt zum nächsten Element in der Warteschlange.Dieser Operator wird überladen, um sowohl Präinkrement- als auch Postinkrementsemantik bereitzustellen. |
|
Ruft einen Verweis auf das aktuelle Element ab. |
|
Ruft einen Zeiger auf das aktuelle Element ab. |
Top
Concurrent_unordered_map-Klasse
Die concurrency::concurrent_unordered_map Klasse ist eine assoziative Container-Klasse, die genau wie die std::unordered_map Klasse, die eine unterschiedliche Länge Sequenz von Elementen des Typs steuert Std < const Key, Ty >.Stellen Sie eine ungeordnete Karte als ein Wörterbuch, das ein Schlüssel-Wert-Paar hinzufügen oder einen Wert von Schlüssel suchen.Diese Klasse ist nützlich, wenn Sie mehrere Threads oder Aufgaben, die gleichzeitig Zugriff auf einen gemeinsamen Container, in sie einfügen oder aktualisieren.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung von concurrent_unordered_map.Dieses Beispiel fügt Zeichentasten im Bereich ['a', ' i'].Da die Reihenfolge der Vorgänge unbestimmt ist, ist der Endwert für jeden Schlüssel auch unbestimmt.Allerdings ist es sicher, das für Einfügevorgänge parallel.
// 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]
*/
Ein Beispiel für die concurrent_unordered_map zum Ausführen einer Zuordnung und Betrieb parallel zu verringern, finden Sie unter Gewusst wie: Paralleles Ausführen von Zuordnungs- und Reduzierungsoperationen.
Unterschiede zwischen Concurrent_unordered_map und unordered_map
Die concurrent_unordered_map-Klasse stimmt mit der unordered_map-Klasse weitgehend überein.Im Folgenden sind die Unterschiede zwischen concurrent_unordered_map und unordered_map aufgeführt:
The erase, bucket, bucket_count, and bucket_size methods are named unsafe_erase, unsafe_bucket, unsafe_bucket_count, and unsafe_bucket_size, respectively.Die unsafe_ Benennungskonvention gibt an, dass diese Methoden nicht sicher sind.Weitere Informationen über die Sicherheit der Parallelität finden Sie unter Vorgänge sicher.
INSERT-Operationen werden nicht ungültig, vorhandenen Zeiger oder Iteratoren, noch ändern sie die Reihenfolge der Elemente, die in der Karte vorhanden.Einfügen und durchlaufen Vorgänge gleichzeitig auftreten können.
concurrent_unordered_mapunterstützt nur Iteration zu übermitteln.
Einfügen nicht ungültig oder aktualisieren die Iteratoren, die von zurückgegebenen equal_range.Einfügemarke kann ungleiche Elemente an das Ende des Bereichs anfügen.Der Begin Iterator verweist auf ein Element gleich.
Zur Vermeidung von Deadlocks, keine Methode der concurrent_unordered_map eine Sperre hält, wenn es die Speicherreservierungsfunktion, Hash-Funktionen oder anderen benutzerdefinierten Code aufruft.Außerdem müssen Sie sicherstellen, dass die Hash-Funktion immer gleich Schlüssel auf den gleichen Wert ausgewertet wird.Optimalen Hash-Funktionen werden Schlüssel über die Hash-Code-Space gleichmäßig verteilen.
Parallelitätssichere Vorgänge
Die concurrent_unordered_map -Klasse können sicher INSERT- und Element-Access-Operationen.INSERT-Operationen nicht vorhandenen Zeiger oder Iteratoren ungültig.Iteratorzugriff und Durchlaufvorgänge sind ebenfalls parallelitätssicher.Die folgende Tabelle enthält häufig verwendete concurrent_unordered_map Methoden und Operatoren, die sicher sind.
count |
find |
||
begin |
empty |
get_allocator |
max_size |
cbegin |
end |
hash_function |
|
cend |
equal_range |
size |
Obwohl die count Methode kann sicher von gleichzeitig ausgeführten Threads aufgerufen werden, die verschiedenen Threads können unterschiedliche Ergebnisse erhalten, wenn Sie ein neuer Wert in den Container gleichzeitig eingefügt wird.
Die folgende Tabelle enthält die häufig verwendete Methoden und Operatoren, die nicht sicher sind.
clear |
max_load_factor |
rehash |
load_factor |
Zusätzlich zu diesen Methoden Methode, die fängt mit unsafe_ ist auch nicht sicher.
Top
Concurrent_unordered_multimap-Klasse
Die concurrency::concurrent_unordered_multimap Klasse ähnelt der concurrent_unordered_map -Klasse, außer dass es für mehrere Werte den gleichen Schlüssel zuordnen kann.Es unterscheidet sich auch von concurrent_unordered_map auf folgende Weise:
Die concurrent_unordered_multimap::insert -Methode gibt einen Iterator statt std::pair<iterator, bool>.
Die concurrent_unordered_multimap Klasse bietet keine operator[] noch die at Methode.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung von concurrent_unordered_multimap.Dieses Beispiel fügt Zeichentasten im Bereich ['a', ' i'].concurrent_unordered_multimapkönnen eine Taste, um mehrere Werte besitzen.
// 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
Concurrent_unordered_set-Klasse
Die concurrency::concurrent_unordered_set Klasse ähnelt der concurrent_unordered_map -Klasse, außer dass er Werte anstelle von Schlüssel-Wert-Paaren verwaltet.Die concurrent_unordered_set Klasse bietet keine operator[] noch die at Methode.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung von concurrent_unordered_set.Dieses Beispiel fügt Zeichenwerte im Bereich ['a', ' i'].Es ist sicher die Einfügungen Parallel auszuführen.
// 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
Concurrent_unordered_multiset-Klasse
Die concurrency::concurrent_unordered_multiset Klasse ähnelt der concurrent_unordered_set -Klasse, mit der Ausnahme, dass es nach doppelten Werten ermöglicht.Es unterscheidet sich auch von concurrent_unordered_set auf folgende Weise:
Die concurrent_unordered_multiset::insert -Methode gibt einen Iterator statt std::pair<iterator, bool>.
Die concurrent_unordered_multiset Klasse bietet keine operator[] noch die at Methode.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung von concurrent_unordered_multiset.Dieses Beispiel fügt Zeichenwerte im Bereich ['a', ' i'].concurrent_unordered_multisetkönnen einen Wert mehrmals ausgeführt.
// 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
combinable-Klasse
Die concurrency::combinable -Klasse stellt eine wiederverwendbare, Thread-Local Storage, mit dem Sie die abgestimmte Berechnungen durchführen und dann die Berechnungen in ein Endergebnis zusammenführen.Sie können sich ein combinable-Objekt wie eine reduction-Variable vorstellen.
Die combinable-Klasse ist nützlich, wenn Sie mit einer Ressource arbeiten, die von mehreren Threads bzw. Aufgaben gemeinsam genutzt wird.Mit der combinable-Klasse können Sie durch Bereitstellen von sperrenfreiem Zugriff auf freigegebene Ressourcen den Freigabezustand ausschließen.Daher bietet diese Klasse eine Alternative zur Verwendung eines Synchronisierungsmechanismus, z. B. eines Mutex, für die Synchronisierung des Zugriffs auf freigegebene Daten von mehreren Threads.
Methoden und Funktionen
Die folgende Tabelle enthält einige wichtige Methoden der combinable-Klasse.Informationen zu allen Methoden der combinable-Klasse finden Sie unter combinable-Klasse.
Methode |
Beschreibung |
---|---|
Ruft einen Verweis auf die lokale Variable ab, die dem aktuellen Threadkontext zugeordnet ist. |
|
Entfernt alle lokalen Threadvariablen aus dem combinable-Objekt. |
|
Verwendet die bereitgestellte combine-Funktion zur Generierung eines endgültigen Werts aus allen lokalen Threadberechnungen. |
Die combinable-Klasse ist eine Vorlagenklasse, die auf der Grundlage des abschließenden zusammengeführten Ergebnisses parametrisiert wird.Wenn Sie den Standardkonstruktor aufrufen, muss der _Ty-Vorlagenparametertyp einen Standardkonstruktor und einen Kopierkonstruktor aufweisen.Wenn der _Ty-Vorlagenparametertyp keinen Standardkonstruktor aufweist, rufen Sie die überladene Version des Konstruktors auf, die eine Initialisierungsfunktion als Parameter akzeptiert.
Sie können weitere Daten in einem combinable-Objekt speichern, nachdem Sie die combine-Methode bzw. die combine_each-Methode aufgerufen haben.Außerdem können Sie die combine-Methode sowie die combine_each-Methode mehrmals aufrufen.Wenn sich in einem combinable-Objekt kein lokaler Wert ändert, führt jeder Aufruf der combine-Methode und der combine_each-Methode zum gleichen Ergebnis.
Beispiele
Beispiele zur Verwendung der combinable-Klasse finden Sie in den folgenden Themen:
Gewusst wie: Verbessern der Leistung mithilfe von combinable
Gewusst wie: Kombinieren von Gruppen mithilfe von combinable
Top
Verwandte Themen
Gewusst wie: Erhöhen der Effizienz mithilfe von parallelen Containern
Zeigt, wie parallele Container verwendet werden, um Daten effizient parallel zu speichern und parallel darauf zuzugreifen.Gewusst wie: Verbessern der Leistung mithilfe von combinable
Zeigt, wie die combinable-Klasse verwendet wird, um den Freigabezustand auszuschließen und so die Leistung zu verbessern.Gewusst wie: Kombinieren von Gruppen mithilfe von combinable
Zeigt, wie eine combine-Funktion verwendet wird, um lokale Thread-Datensätze zusammenzuführen.Parallel Patterns Library (PPL)
Beschreibt die PPL, die ein obligatorisches Programmiermodell bereitstellt, das Skalierbarkeit und Benutzerfreundlichkeit beim Entwickeln gleichzeitiger Anwendungen unterstützt.
Verweis
concurrent_unordered_map-Klasse
concurrent_unordered_multimap-Klasse
concurrent_unordered_set-Klasse