Freigeben über


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.

Dd504906.collapse_all(de-de,VS.110).gifUnterschiede 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.

Dd504906.collapse_all(de-de,VS.110).gifParallelitä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.

at

end

operator[]

begin

front

push_back

back

grow_by

rbegin

capacity

grow_to_at_least

rend

empty

max_size

size

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.

assign

reserve

clear

resize

operator=

shrink_to_fit

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.

Dd504906.collapse_all(de-de,VS.110).gifAusnahmesicherheit

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.

Dd504906.collapse_all(de-de,VS.110).gifUnterschiede 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.

Dd504906.collapse_all(de-de,VS.110).gifParallelitä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.

empty

push

get_allocator

try_pop

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.

clear

unsafe_end

unsafe_begin

unsafe_size

Dd504906.collapse_all(de-de,VS.110).gifIteratorunterstü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

operator++

Wechselt zum nächsten Element in der Warteschlange.Dieser Operator wird überladen, um sowohl Präinkrement- als auch Postinkrementsemantik bereitzustellen.

operator*

Ruft einen Verweis auf das aktuelle Element ab.

operator->

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.

Dd504906.collapse_all(de-de,VS.110).gifUnterschiede 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.

Dd504906.collapse_all(de-de,VS.110).gifParallelitä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.

at

count

find

key_eq

begin

empty

get_allocator

max_size

cbegin

end

hash_function

operator[]

cend

equal_range

Einfügen

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

operator=

Swap

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.

Dd504906.collapse_all(de-de,VS.110).gifMethoden 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

local

Ruft einen Verweis auf die lokale Variable ab, die dem aktuellen Threadkontext zugeordnet ist.

clear

Entfernt alle lokalen Threadvariablen aus dem combinable-Objekt.

combine

combine_each

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.

Dd504906.collapse_all(de-de,VS.110).gifBeispiele

Beispiele zur Verwendung der combinable-Klasse finden Sie in den folgenden Themen:

Top

Verwandte Themen

Verweis

concurrent_vector-Klasse

concurrent_queue-Klasse

concurrent_unordered_map-Klasse

concurrent_unordered_multimap-Klasse

concurrent_unordered_set-Klasse

concurrent_unordered_multiset-Klasse

combinable-Klasse