Parallele Container und Objekte
Die Parallel Patterns Library (PPL) enthält mehrere Container und Objekte, die threadsicheren Zugriff auf ihre Elemente bieten.
Ein gleichzeitiger Container bietet gleichzeitigen sicheren Zugriff auf die wichtigsten Vorgänge. Hier sind Parallelitätssichere Zeiger oder Iteratoren immer gültig. Es ist keine Garantie für die Elementinitialisierung oder eine bestimmte Traversalreihenfolge. Die Funktionalität dieser Container ähnelt denen, die von der C++-Standardbibliothek bereitgestellt werden. Beispielsweise ähnelt die Parallelitätsklasse::concurrent_vector Klasse der std::vector-Klasse , mit der Ausnahme, dass die concurrent_vector
Klasse Elemente parallel anfügen kann. Verwenden Sie gleichzeitige Container, wenn Sie über parallelen Code verfügen, der sowohl Lese- als auch Schreibzugriff auf denselben Container erfordert.
Ein gleichzeitiges Objekt wird gleichzeitig für Komponenten freigegeben. Ein Prozess, der den Zustand eines gleichzeitigen Objekts parallel berechnet, erzeugt dasselbe Ergebnis wie ein anderer Prozess, der denselben Zustand fortlaufend berechnet. Die Parallelität::kombinationsfähige Klasse ist ein Beispiel für einen gleichzeitigen Objekttyp. Mit der combinable
Klasse können Sie Berechnungen parallel durchführen und diese Berechnungen dann in einem Endergebnis kombinieren. Verwenden Sie gleichzeitige Objekte, wenn Sie andernfalls einen Synchronisierungsmechanismus verwenden würden, z. B. einen Mutex, um den Zugriff auf eine freigegebene Variable oder Ressource zu synchronisieren.
Abschnitte
In diesem Thema werden die folgenden parallelen Container und Objekte ausführlich beschrieben.
Gleichzeitige Container:
Gleichzeitige Objekte:
concurrent_vector-Klasse
Die Parallelität::concurrent_vector Klasse ist eine Sequenzcontainerklasse, mit der Sie wie die Std::Vector-Klasse zufällig auf die zugehörigen Elemente zugreifen können. Die concurrent_vector
Klasse ermöglicht Parallelitätssichere Anfüge- und Elementzugriffsvorgänge. Anfügevorgänge können vorhandene Zeiger oder Iteratoren nicht ungültig machen. Iteratorzugriff und Traversalvorgänge sind ebenfalls parallel. Hier sind Parallelitätssichere Zeiger oder Iteratoren immer gültig. Es ist keine Garantie für die Elementinitialisierung oder eine bestimmte Traversalreihenfolge.
Unterschiede zwischen concurrent_vector und Vektoren
Die concurrent_vector
Klasse ähnelt vector
der Klasse genau. Die Komplexität von Anfüge-, Elementzugriffs- und Iteratorzugriffsvorgängen für ein concurrent_vector
Objekt ist identisch mit einem vector
Objekt. Die folgenden Punkte veranschaulichen, wo concurrent_vector
sich die Unterschiede unterscheiden vector
:
Anfüge-, Elementzugriff, Iteratorzugriff und Iterator-Traversalvorgänge für ein
concurrent_vector
Objekt sind parallel.Sie können Elemente nur am Ende eines
concurrent_vector
Objekts hinzufügen. Dieconcurrent_vector
Klasse stellt dieinsert
Methode nicht bereit.Ein
concurrent_vector
Objekt verwendet beim Anfügen keine Bewegungssemantik .Die
concurrent_vector
Klasse stellt weder die Methoden nochpop_back
dieerase
Methoden bereit. Verwenden Sie wie bei diesem Verfahrenvector
die clear-Methode , um alle Elemente aus einemconcurrent_vector
Objekt zu entfernen.Die
concurrent_vector
Klasse speichert ihre Elemente nicht zusammenhängend im Speicher. Daher können Sie dieconcurrent_vector
Klasse nicht auf alle Arten verwenden, wie Sie ein Array verwenden können. For example, for a variable namedv
of typeconcurrent_vector
, the expression&v[0]+2
produces undefined behavior.Die
concurrent_vector
Klasse definiert die methoden grow_by und grow_to_at_least . Diese Methoden ähneln der Resize-Methode , mit der Ausnahme, dass sie Parallelitätssicher sind.Ein
concurrent_vector
Objekt verschlegt seine Elemente nicht, wenn Sie es anfügen oder dessen Größe ändern. Dadurch können vorhandene Zeiger und Iteratoren während gleichzeitiger Vorgänge wieder Standard gültig sein.Die Laufzeit definiert keine spezielle Version vom
concurrent_vector
Typbool
.
Parallelitäts-Tresor Vorgänge
Alle Methoden, die die Größe eines concurrent_vector
Objekts anfügen oder vergrößern oder auf ein Element in einem concurrent_vector
Objekt zugreifen, sind Parallelitätssicher. Hier sind Parallelitätssichere Zeiger oder Iteratoren immer gültig. Es ist keine Garantie für die Elementinitialisierung oder eine bestimmte Traversalreihenfolge. Die Ausnahme von dieser Regel ist die resize
Methode.
Die folgende Tabelle zeigt die gängigen concurrent_vector
Methoden und Operatoren, die Parallelitätssicher sind.
Vorgänge, die von der Laufzeit zur Kompatibilität mit der C++-Standardbibliothek bereitgestellt werden, reserve
sind z. B. nicht parallel. In der folgenden Tabelle sind die gängigen Methoden und Operatoren aufgeführt, die nicht parallel sind.
Vorgänge, die den Wert vorhandener Elemente ändern, sind nicht parallel. Verwenden Sie ein Synchronisierungsobjekt wie ein reader_writer_lock-Objekt , um gleichzeitige Lese- und Schreibvorgänge mit demselben Datenelement zu synchronisieren. Weitere Informationen zu Synchronisierungsobjekten finden Sie unter "Synchronisierungsdatenstrukturen".
Wenn Sie vorhandenen Code konvertieren, der zur Verwendung verwendet concurrent_vector
wirdvector
, können gleichzeitige Vorgänge dazu führen, dass sich das Verhalten Ihrer Anwendung ändert. Betrachten Sie beispielsweise das folgende Programm, das gleichzeitig zwei Aufgaben für ein concurrent_vector
Objekt ausführt. Die erste Aufgabe fügt zusätzliche Elemente an ein concurrent_vector
Objekt an. Der zweite Vorgang berechnet die Summe aller Elemente im selben 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;
}
);
}
Obwohl die end
Methode parallel ist, führt ein gleichzeitiger Aufruf der push_back-Methode dazu, dass sich der Wert ändert, der zurückgegeben end
wird. Die Anzahl der Elemente, die der Iterator durchläuft, ist unbestimmt. Daher kann dieses Programm jedes Mal ein anderes Ergebnis erzielen, wenn Sie es ausführen. Wenn der Elementtyp nicht trivial ist, ist es möglich, dass eine Racebedingung zwischen push_back
und end
Aufrufen vorhanden ist. Die end
Methode gibt möglicherweise ein zugeordnetes Element zurück, aber nicht vollständig initialisiert.
Ausnahme Tresor ty
Wenn ein Wachstums- oder Zuordnungsvorgang eine Ausnahme auslöst, wird der Status des concurrent_vector
Objekts ungültig. Das Verhalten eines concurrent_vector
Objekts, das sich in einem ungültigen Zustand befindet, ist nicht definiert, sofern nicht anders angegeben. Der Destruktor gibt jedoch immer den Speicher frei, den das Objekt zuweist, auch wenn sich das Objekt in einem ungültigen Zustand befindet.
Der Datentyp der Vektorelemente T
muss die folgenden Anforderungen erfüllen. Andernfalls ist das Verhalten der concurrent_vector
Klasse nicht definiert.
Der Destruktor darf nicht ausgelöst werden.
Wenn der Standard- oder Kopierkonstruktor ausgelöst wird, darf der Destruktor nicht mithilfe der
virtual
Schlüsselwort (keyword) deklariert werden, und er muss ordnungsgemäß mit null initialisiertem Arbeitsspeicher funktionieren.
concurrent_queue-Klasse
Mit der Parallelität::concurrent_queue Klasse können Sie genau wie die std::queue-Klasse auf die Vorder- und Rückseitenelemente zugreifen. Die concurrent_queue
Klasse ermöglicht Parallelitätssichere Enqueue- und Dequeue-Vorgänge. Hier sind Parallelitätssichere Zeiger oder Iteratoren immer gültig. Es ist keine Garantie für die Elementinitialisierung oder eine bestimmte Traversalreihenfolge. Die concurrent_queue
Klasse bietet auch Iteratorunterstützung, die nicht parallel ist.
Unterschiede zwischen concurrent_queue und Warteschlange
Die concurrent_queue
Klasse ähnelt queue
der Klasse genau. Die folgenden Punkte veranschaulichen, wo concurrent_queue
sich die Unterschiede unterscheiden queue
:
Enqueue- und Dequeue-Vorgänge für ein
concurrent_queue
Objekt sind parallelsicher.Die
concurrent_queue
Klasse bietet Iteratorunterstützung, die nicht parallel ist.Die
concurrent_queue
Klasse stellt weder die Methoden nochpop
diefront
Methoden bereit. Dieconcurrent_queue
Klasse ersetzt diese Methoden durch Definieren der try_pop-Methode .Die
concurrent_queue
Klasse stellt dieback
Methode nicht bereit. Daher können Sie nicht auf das Ende der Warteschlange verweisen.Die
concurrent_queue
Klasse stellt die unsafe_size-Methode anstelle dersize
Methode bereit. Dieunsafe_size
Methode ist nicht parallel.
Parallelitäts-Tresor Vorgänge
Alle Methoden, die von einem concurrent_queue
Objekt in eine Queue queue oder dequeue führen, sind parallelsicher. Hier sind Parallelitätssichere Zeiger oder Iteratoren immer gültig. Es ist keine Garantie für die Elementinitialisierung oder eine bestimmte Traversalreihenfolge.
Die folgende Tabelle zeigt die gängigen concurrent_queue
Methoden und Operatoren, die Parallelitätssicher sind.
Obwohl die empty
Methode parallel ist, kann ein gleichzeitiger Vorgang dazu führen, dass die Warteschlange vergrößert oder verkleinern wird, bevor die empty
Methode zurückgegeben wird.
In der folgenden Tabelle sind die gängigen Methoden und Operatoren aufgeführt, die nicht parallel sind.
Iteratorunterstützung
Die concurrent_queue
Iteratoren bieten keine Parallelitätssicher. Es wird empfohlen, diese Iteratoren nur für das Debuggen zu verwenden.
Ein concurrent_queue
Iterator durchläuft Elemente nur in Vorwärtsrichtung. In der folgenden Tabelle sind die Operatoren aufgeführt, die von jedem Iterator unterstützt werden.
Operator | Beschreibung |
---|---|
operator++ |
Wechselt zum nächsten Element in der Warteschlange. Dieser Operator wird überladen, um sowohl präinkrementierte als auch postinkrementierte Semantik bereitzustellen. |
operator* |
Ruft einen Verweis auf das aktuelle Element ab. |
operator-> |
Ruft einen Zeiger auf das aktuelle Element ab. |
concurrent_unordered_map-Klasse
Die Parallelität::concurrent_unordered_map Klasse ist eine assoziative Containerklasse, die genau wie die Klasse std::unordered_map eine unterschiedliche Abfolge von Elementen vom Typ "std::p air<const Key" (Ty>) steuert. Stellen Sie sich eine ungeordnete Karte als Wörterbuch vor, das Sie einem Schlüssel- und Wertpaar hinzufügen oder einen Wert nach Schlüssel nachschlagen können. Diese Klasse ist nützlich, wenn Sie über mehrere Threads oder Aufgaben verfügen, die gleichzeitig auf einen freigegebenen Container zugreifen, darin einfügen oder aktualisieren müssen.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung concurrent_unordered_map
. In diesem Beispiel werden Zeichentasten in den Bereich ['a', 'i'] eingefügt. Da die Reihenfolge der Vorgänge nicht festgelegt ist, ist auch der endgültige Wert für jeden Schlüssel unbestimmt. Es ist jedoch sicher, die Einfügungen parallel durchzuführen.
// 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, das zum Ausführen einer Karte und zur Reduzierung des Vorgangs parallel verwendet concurrent_unordered_map
wird, finden Sie unter How to: Perform Map and Reduce Operations in Parallel.
Unterschiede zwischen concurrent_unordered_map und unordered_map
Die concurrent_unordered_map
Klasse ähnelt unordered_map
der Klasse genau. Die folgenden Punkte veranschaulichen, wo concurrent_unordered_map
sich die Unterschiede unterscheiden unordered_map
:
Die
erase
,bucket
,bucket_count
, undbucket_size
Methoden sind benanntunsafe_erase
,unsafe_bucket
,unsafe_bucket_count
, undunsafe_bucket_size
, bzw. . Dieunsafe_
Benennungskonvention gibt an, dass diese Methoden nicht parallel sind. Weitere Informationen zur Parallelitätssicherheit finden Sie unter Concurrency-Tresor Operations.Einfügevorgänge ungültig machen weder vorhandene Zeiger noch Iteratoren, noch ändern sie die Reihenfolge der Elemente, die bereits in der Karte vorhanden sind. Einfüge- und Durchlaufvorgänge können gleichzeitig ausgeführt werden.
concurrent_unordered_map
unterstützt nur die Weiterleitungsiteration.Die Einfügemarke wird nicht ungültig oder aktualisiert die Iteratoren, die von
equal_range
. Das Einfügen kann ungleiche Elemente an das Ende des Bereichs anfügen. Der Anfangs iterator zeigt auf ein gleichheitselement.
Um Deadlock zu vermeiden, enthält keine Methode eine concurrent_unordered_map
Sperre, wenn sie den Speicherzuordnungs-, Hashfunktionen oder anderen benutzerdefinierten Code aufruft. Außerdem müssen Sie sicherstellen, dass die Hashfunktion immer gleich Schlüssel zum gleichen Wert auswertet. Die besten Hashfunktionen verteilen Schlüssel einheitlich über den Hashcodebereich.
Parallelitäts-Tresor Vorgänge
Die concurrent_unordered_map
Klasse ermöglicht Parallelitätssichere Einfüge- und Elementzugriffsvorgänge. Einfügevorgänge können vorhandene Zeiger oder Iteratoren nicht ungültig machen. Iteratorzugriff und Traversalvorgänge sind ebenfalls parallel. Hier sind Parallelitätssichere Zeiger oder Iteratoren immer gültig. Es ist keine Garantie für die Elementinitialisierung oder eine bestimmte Traversalreihenfolge. In der folgenden Tabelle sind die häufig verwendeten concurrent_unordered_map
Methoden und Operatoren aufgeführt, die Parallelitätssicher sind.
Obwohl die count
Methode sicher von gleichzeitig ausgeführten Threads aufgerufen werden kann, können unterschiedliche Threads unterschiedliche Ergebnisse erhalten, wenn ein neuer Wert gleichzeitig in den Container eingefügt wird.
In der folgenden Tabelle sind die häufig verwendeten Methoden und Operatoren aufgeführt, die nicht parallel sind.
Zusätzlich zu diesen Methoden ist jede Methode, die beginnt unsafe_
, auch nicht parallel.
concurrent_unordered_multimap-Klasse
Die Parallelität::concurrent_unordered_multimap Klasse ähnelt der concurrent_unordered_map
Klasse eng, mit der Ausnahme, dass mehrere Werte demselben Schlüssel zugeordnet werden können. Es unterscheidet sich auch von concurrent_unordered_map
den folgenden Methoden:
Die concurrent_unordered_multimap::insert-Methode gibt einen Iterator anstelle von
std::pair<iterator, bool>
.Die
concurrent_unordered_multimap
Klasse stellt weder die Methode noch dieat
Methode bereitoperator[]
.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung concurrent_unordered_multimap
. In diesem Beispiel werden Zeichentasten in den Bereich ['a', 'i'] eingefügt. concurrent_unordered_multimap
ermöglicht es einem Schlüssel, mehrere Werte zu haben.
// 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]
*/
concurrent_unordered_set-Klasse
Die Parallelität::concurrent_unordered_set Klasse ähnelt der concurrent_unordered_map
Klasse eng, mit der Ausnahme, dass Werte anstelle von Schlüssel- und Wertpaaren verwaltet werden. Die concurrent_unordered_set
Klasse stellt weder die Methode noch die at
Methode bereitoperator[]
.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung concurrent_unordered_set
. In diesem Beispiel werden Zeichenwerte in den Bereich ['a', 'i'] eingefügt. Es ist sicher, die Einfügungen parallel durchzufü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]
*/
concurrent_unordered_multiset-Klasse
Die Parallelität::concurrent_unordered_multiset-Klasse ähnelt der concurrent_unordered_set
Klasse eng, mit der Ausnahme, dass sie doppelte Werte zulässt. Es unterscheidet sich auch von concurrent_unordered_set
den folgenden Methoden:
Die concurrent_unordered_multiset::insert-Methode gibt einen Iterator anstelle von
std::pair<iterator, bool>
.Die
concurrent_unordered_multiset
Klasse stellt weder die Methode noch dieat
Methode bereitoperator[]
.
Das folgende Beispiel zeigt die grundlegende Struktur für die Verwendung concurrent_unordered_multiset
. In diesem Beispiel werden Zeichenwerte in den Bereich ['a', 'i'] eingefügt. concurrent_unordered_multiset
aktiviert, dass ein Wert mehrmals auftritt.
// 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]
*/
combinable-Klasse
Die parallele Klasse::kombinationsfähige Klasse bietet wiederverwendbaren , threadlokalen Speicher, mit dem Sie differenzierte Berechnungen durchführen und diese Berechnungen dann in ein Endergebnis zusammenführen können. Stellen Sie sich ein combinable
-Objekt wie eine Reduktionsvariable vor.
Die combinable
Klasse ist nützlich, wenn Sie über eine Ressource verfügen, die von mehreren Threads oder Vorgängen gemeinsam genutzt wird. Die combinable
Klasse hilft Ihnen, den freigegebenen Zustand zu beseitigen, indem Sie zugriff auf freigegebene Ressourcen auf sperrfreie Weise bereitstellen. Daher bietet diese Klasse eine Alternative zur Verwendung eines Synchronisierungsmechanismus, z. B. einen Mutex, um den Zugriff auf freigegebene Daten aus mehreren Threads zu synchronisieren.
Methoden und Features
In der folgenden Tabelle sind einige der wichtigen Methoden der combinable
Klasse aufgeführt. Weitere Informationen zu allen combinable
Klassenmethoden finden Sie unter "Kombinierbare Klasse".
Methode | Beschreibung |
---|---|
local | Ruft einen Verweis auf die lokale Variable ab, die dem aktuellen Threadkontext zugeordnet ist. |
clear | Entfernt alle threadlokalen Variablen aus dem combinable Objekt. |
combine combine_each |
Verwendet die bereitgestellte Kombinationsfunktion, um einen endgültigen Wert aus der Gruppe aller threadlokalen Berechnungen zu generieren. |
Die combinable
Klasse ist eine Vorlagenklasse, die für das endgültige zusammengeführte Ergebnis parametrisiert wird. Wenn Sie den Standardkonstruktor aufrufen, muss der T
Vorlagenparametertyp über einen Standardkonstruktor und einen Kopierkonstruktor verfügen. Wenn der T
Vorlagenparametertyp keinen Standardkonstruktor aufweist, rufen Sie die überladene Version des Konstruktors auf, die eine Initialisierungsfunktion als Parameter verwendet.
Sie können zusätzliche Daten in einem combinable
Objekt speichern, nachdem Sie die Combine - oder combine_each-Methoden aufgerufen haben. Sie können die combine
Methoden combine_each
auch mehrmals aufrufen. Wenn sich kein lokaler Wert in einem combinable
Objekt ändert, erzeugen die combine
Methoden combine_each
jedes Mal, wenn sie aufgerufen werden, dasselbe Ergebnis.
Beispiele
Beispiele zur Verwendung der combinable
Klasse finden Sie in den folgenden Themen:
Vorgehensweise: Verbessern der Leistung mithilfe von combinable
Vorgehensweise: Kombinieren von Gruppen mithilfe von combinable
Verwandte Themen
Vorgehensweise: Erhöhen der Effizienz mithilfe von parallelen Containern
Zeigt, wie parallele Container verwendet werden, um Daten effizient zu speichern und parallel darauf zuzugreifen.
Vorgehensweise: Verbessern der Leistung mithilfe von combinable
Zeigt, wie Sie die Klasse verwenden, um den combinable
freigegebenen Zustand zu beseitigen und dadurch die Leistung zu verbessern.
Vorgehensweise: Kombinieren von Gruppen mithilfe von combinable
Zeigt, wie eine Funktion zum Zusammenführen von threadlokalen Datensätzen verwendet combine
wird.
Parallel Patterns Library (PPL)
Beschreibt die PPL, die ein imperatives Programmiermodell bereitstellt, das Skalierbarkeit und Benutzerfreundlichkeit für die Entwicklung gleichzeitiger Anwendungen fördert.
Verweis
concurrent_unordered_map-Klasse
concurrent_unordered_multimap-Klasse
concurrent_unordered_set-Klasse