Równoległe kontenery oraz obiekty
Biblioteka wzorców równoległych (PPL) zawiera kilka kontenerów i obiektów, które zapewniają bezpieczny wątkowo dostęp do ich elementów.
Kontener współbieżny zapewnia bezpieczny dostęp współbieżności do najważniejszych operacji. W tym miejscu wskaźniki lub iteratory są zawsze prawidłowe. Nie jest to gwarancja inicjowania elementów ani określonej kolejności przechodzenia. Funkcjonalność tych kontenerów przypomina te, które są dostarczane przez bibliotekę standardową języka C++. Na przykład klasa concurrency::concurrent_vector przypomina klasę std::vector, z tą różnicą, że concurrent_vector
klasa umożliwia równoległe dołączanie elementów. Używaj współbieżnych kontenerów, gdy masz kod równoległy, który wymaga zarówno dostępu do odczytu, jak i zapisu do tego samego kontenera.
Współbieżny obiekt jest współużytkowany współbieżnie między składnikami. Proces, który oblicza stan współbieżnego obiektu równolegle, daje taki sam wynik jak inny proces, który oblicza ten sam stan szeregowo. Współbieżność ::combinable , klasa jest jednym z przykładów współbieżnego typu obiektu. Klasa combinable
umożliwia równoległe wykonywanie obliczeń, a następnie łączenie tych obliczeń w końcowy wynik. Użyj obiektów współbieżnych, jeśli w przeciwnym razie użyjesz mechanizmu synchronizacji, na przykład mutexu, aby zsynchronizować dostęp do współużytkowanej zmiennej lub zasobu.
Sekcje
W tym temacie opisano szczegółowo następujące kontenery równoległe i obiekty.
Kontenery współbieżne:
Współbieżne obiekty:
concurrent_vector — Klasa
Klasa concurrency::concurrent_vector to klasa kontenera sekwencji, która podobnie jak klasa std::vector umożliwia losowy dostęp do jego elementów. Klasa concurrent_vector
umożliwia bezpieczne dołączanie współbieżności i operacje dostępu do elementów. Operacje dołączania nie unieważniają istniejących wskaźników ani iteratorów. Operacje dostępu iteracyjnego i przechodzenia są również bezpieczne ze współbieżnością. W tym miejscu wskaźniki lub iteratory są zawsze prawidłowe. Nie jest to gwarancja inicjowania elementów ani określonej kolejności przechodzenia.
Różnice między concurrent_vector a wektorem
Klasa concurrent_vector
jest ściśle podobna do vector
klasy. Złożoność operacji dostępu dołączania, elementu i iteratora w concurrent_vector
obiekcie jest taka sama jak w przypadku vector
obiektu. Poniższe punkty ilustrują różnice concurrent_vector
między vector
elementami :
Dołączanie, dostęp do elementów, dostęp iteracyjny i operacje przechodzenia iteratora
concurrent_vector
na obiekcie są bezpieczne ze współbieżnością.Elementy można dodawać tylko na końcu
concurrent_vector
obiektu. Klasaconcurrent_vector
nie udostępniainsert
metody .Obiekt
concurrent_vector
nie używa semantyki przenoszenia podczas dołączania do niego.Klasa
concurrent_vector
nie udostępniaerase
metod lubpop_back
. Podobnie jak w przypadkuvector
metody , użyj metody clear , aby usunąć wszystkie elementy zconcurrent_vector
obiektu.Klasa
concurrent_vector
nie przechowuje swoich elementów w pamięci. W związku z tym nie można używaćconcurrent_vector
klasy na wszystkie sposoby, które można użyć tablicy. Na przykład w przypadku zmiennej o nazwiev
typuconcurrent_vector
wyrażenie&v[0]+2
generuje niezdefiniowane zachowanie.Klasa
concurrent_vector
definiuje metody grow_by i grow_to_at_least . Te metody przypominają metodę zmiany rozmiaru, z tą różnicą, że są bezpieczne ze współbieżnością.Obiekt
concurrent_vector
nie przenosi swoich elementów podczas dołączania do niego ani zmienia jego rozmiaru. Dzięki temu istniejące wskaźniki i iteratory pozostają prawidłowe podczas operacji współbieżnych.Środowisko uruchomieniowe nie definiuje wyspecjalizowanej wersji
concurrent_vector
typubool
.
Operacje współbieżności Sejf
Wszystkie metody dołączane do obiektu lub zwiększające ich rozmiar concurrent_vector
lub uzyskują dostęp do elementu w concurrent_vector
obiekcie, są bezpieczne dla współbieżności. W tym miejscu wskaźniki lub iteratory są zawsze prawidłowe. Nie jest to gwarancja inicjowania elementów ani określonej kolejności przechodzenia. Wyjątkiem od tej reguły jest resize
metoda .
W poniższej tabeli przedstawiono typowe concurrent_vector
metody i operatory, które są bezpieczne ze współbieżnością.
Operacje zapewniane przez środowisko uruchomieniowe pod kątem zgodności ze standardową biblioteką języka C++, na przykład reserve
, nie są bezpieczne dla współbieżności. W poniższej tabeli przedstawiono typowe metody i operatory, które nie są bezpieczne ze współbieżnością.
Operacje modyfikujące wartość istniejących elementów nie są bezpieczne ze współbieżnością. Użyj obiektu synchronizacji, takiego jak obiekt reader_writer_lock , aby zsynchronizować współbieżne operacje odczytu i zapisu do tego samego elementu danych. Aby uzyskać więcej informacji na temat obiektów synchronizacji, zobacz Struktury danych synchronizacji.
Podczas konwertowania istniejącego kodu używanego vector
do użycia concurrent_vector
operacji współbieżnych może spowodować zmianę zachowania aplikacji. Rozważmy na przykład następujący program, który współbieżnie wykonuje dwa zadania na concurrent_vector
obiekcie. Pierwsze zadanie dołącza dodatkowe elementy do concurrent_vector
obiektu. Drugie zadanie oblicza sumę wszystkich elementów w tym samym obiekcie.
// 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;
}
);
}
end
Mimo że metoda jest bezpieczna współbieżnością, współbieżne wywołanie metody push_back powoduje zmianę wartości zwracanej przez end
metodę . Liczba elementów przechodzenia iteratora jest nieokreślona. W związku z tym ten program może wygenerować inny wynik za każdym razem, gdy go uruchomisz. Gdy typ elementu jest nietrywialny, możliwe jest istnienie warunku wyścigu między wywołaniami push_back
i end
. Metoda end
może zwrócić przydzielony element, ale nie w pełni zainicjowany.
Wyjątek Sejf ty
Jeśli operacja wzrostu lub przypisania zgłasza wyjątek, stan concurrent_vector
obiektu staje się nieprawidłowy. Zachowanie concurrent_vector
obiektu, który jest w nieprawidłowym stanie, jest niezdefiniowane, chyba że określono inaczej. Jednak destruktor zawsze zwalnia pamięć przydzielaną przez obiekt, nawet jeśli obiekt jest w nieprawidłowym stanie.
Typ danych elementów wektorów musi T
spełniać następujące wymagania. W przeciwnym razie zachowanie concurrent_vector
klasy jest niezdefiniowane.
Destruktor nie może rzucać.
Jeśli zgłasza domyślny lub kopiowany konstruktor, destruktor nie może być zadeklarowany za pomocą słowa kluczowego
virtual
i musi działać poprawnie z pamięcią zerową.
[Top]
concurrent_queue — Klasa
Klasa concurrency::concurrent_queue , podobnie jak klasa std::queue , umożliwia dostęp do jej elementów przednich i tylnych. Klasa concurrent_queue
umożliwia wykonywanie operacji kolejki i dequeue w trybie współbieżności. W tym miejscu wskaźniki lub iteratory są zawsze prawidłowe. Nie jest to gwarancja inicjowania elementów ani określonej kolejności przechodzenia. Klasa concurrent_queue
zapewnia również obsługę iteratora, która nie jest bezpieczna współbieżności.
Różnice między concurrent_queue i kolejką
Klasa concurrent_queue
jest ściśle podobna do queue
klasy. Poniższe punkty ilustrują różnice concurrent_queue
między queue
elementami :
Operacje kolejki i dequeue na
concurrent_queue
obiekcie są bezpieczne ze współbieżnością.Klasa
concurrent_queue
zapewnia obsługę iteratora, która nie jest bezpieczna współbieżności.Klasa
concurrent_queue
nie udostępniafront
metod lubpop
. Klasaconcurrent_queue
zastępuje te metody, definiując metodę try_pop .Klasa
concurrent_queue
nie udostępniaback
metody . W związku z tym nie można odwołać się do końca kolejki.Klasa
concurrent_queue
udostępnia metodę unsafe_size zamiastsize
metody . Metodaunsafe_size
nie jest bezpieczna współbieżności.
Operacje współbieżności Sejf
Wszystkie metody w kolejce do lub dequeue z concurrent_queue
obiektu są bezpieczne ze współbieżnością. W tym miejscu wskaźniki lub iteratory są zawsze prawidłowe. Nie jest to gwarancja inicjowania elementów ani określonej kolejności przechodzenia.
W poniższej tabeli przedstawiono typowe concurrent_queue
metody i operatory, które są bezpieczne ze współbieżnością.
empty
Mimo że metoda jest bezpieczna współbieżnością, równoczesna operacja może spowodować wzrost lub zmniejszenie kolejki przed zwróceniem empty
metody.
W poniższej tabeli przedstawiono typowe metody i operatory, które nie są bezpieczne ze współbieżnością.
Obsługa iteratora
Zapewnia concurrent_queue
iteratory, które nie są bezpieczne ze współbieżnością. Zalecamy używanie tych iteratorów tylko do debugowania.
concurrent_queue
Iterator przechodzi tylko w kierunku przodu. W poniższej tabeli przedstawiono operatory obsługiwane przez poszczególne iteratory.
Operator | opis |
---|---|
operator++ |
Przechodzi do następnego elementu w kolejce. Ten operator jest przeciążony w celu zapewnienia semantyki przyrostowej i po przyrostowej. |
operator* |
Pobiera odwołanie do bieżącego elementu. |
operator-> |
Pobiera wskaźnik do bieżącego elementu. |
[Top]
concurrent_unordered_map — Klasa
Klasa concurrency::concurrent_unordered_map jest klasą kontenera kojarzącą, która podobnie jak klasa std::unordered_map kontroluje różną długość sekwencji elementów typu std::p air<const Key, Ty>. Mapę nieurządkowaną można traktować jako słownik, do której można dodać parę klucz i wartość lub wyszukać wartość według klucza. Ta klasa jest przydatna, gdy masz wiele wątków lub zadań, które muszą jednocześnie uzyskiwać dostęp do udostępnionego kontenera, wstawiać do niego lub aktualizować.
W poniższym przykładzie przedstawiono podstawową strukturę używania programu concurrent_unordered_map
. Ten przykład wstawia klucze znaków w zakresie ['a', 'i']. Ponieważ kolejność operacji jest niezdefiniowana, ostateczna wartość każdego klucza jest również niezdefiniowana. Jednak bezpieczne jest równoległe wykonywanie wstawiania.
// 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]
*/
Aby zapoznać się z przykładem używanym concurrent_unordered_map
do równoległego wykonywania operacji mapowania i redukcji, zobacz Jak wykonywać operacje mapowania i redukcji równolegle.
Różnice między concurrent_unordered_map i unordered_map
Klasa concurrent_unordered_map
jest ściśle podobna do unordered_map
klasy. Poniższe punkty ilustrują różnice concurrent_unordered_map
między unordered_map
elementami :
Metody
erase
,bucket
,bucket_count
ibucket_size
mają odpowiednio nazwyunsafe_erase
,unsafe_bucket
,unsafe_bucket_count
iunsafe_bucket_size
. Konwencjaunsafe_
nazewnictwa wskazuje, że te metody nie są bezpieczne ze współbieżnością. Aby uzyskać więcej informacji na temat bezpieczeństwa współbieżności, zobacz Operacje współbieżności Sejf.Operacje wstawiania nie unieważniają istniejących wskaźników ani iteratorów ani nie zmieniają kolejności elementów, które już istnieją na mapie. Operacje wstawiania i przechodzenia mogą wystąpić współbieżnie.
concurrent_unordered_map
obsługuje tylko iterację przesyłania dalej.Wstawienie nie unieważnia ani nie aktualizuje iteratorów zwracanych przez
equal_range
element . Wstawianie może dołączać nierówne elementy na końcu zakresu. Iterator początkowy wskazuje równy element.
Aby uniknąć zakleszczenia, żadna metoda blokady concurrent_unordered_map
nie jest przechowywana, gdy wywołuje alokator pamięci, funkcje skrótu lub inny kod zdefiniowany przez użytkownika. Ponadto należy upewnić się, że funkcja skrótu zawsze ocenia równe klucze do tej samej wartości. Najlepsze funkcje skrótu równomiernie dystrybuują klucze w przestrzeni kodu skrótu.
Operacje współbieżności Sejf
Klasa concurrent_unordered_map
umożliwia bezpieczne wstawianie współbieżności i operacje dostępu do elementów. Operacje wstawiania nie unieważniają istniejących wskaźników ani iteratorów. Operacje dostępu iteracyjnego i przechodzenia są również bezpieczne ze współbieżnością. W tym miejscu wskaźniki lub iteratory są zawsze prawidłowe. Nie jest to gwarancja inicjowania elementów ani określonej kolejności przechodzenia. W poniższej tabeli przedstawiono powszechnie używane concurrent_unordered_map
metody i operatory, które są bezpieczne ze współbieżnością.
count
Mimo że metoda może być wywoływana bezpiecznie z równoczesnych uruchomionych wątków, różne wątki mogą odbierać różne wyniki, jeśli nowa wartość jest jednocześnie wstawiana do kontenera.
W poniższej tabeli przedstawiono powszechnie używane metody i operatory, które nie są bezpieczne ze współbieżnością.
Oprócz tych metod każda metoda rozpoczynająca się od unsafe_
nie jest również bezpieczna współbieżności.
[Top]
concurrent_unordered_multimap — Klasa
Klasa concurrency::concurrent_unordered_multimap jest ściśle podobna concurrent_unordered_map
do klasy, z tą różnicą, że umożliwia mapowania wielu wartości na ten sam klucz. Różni się on również od concurrent_unordered_map
następujących sposobów:
Metoda concurrent_unordered_multimap::insert zwraca iterator zamiast
std::pair<iterator, bool>
.Klasa
concurrent_unordered_multimap
nie udostępniaoperator[]
aniat
metody .
W poniższym przykładzie przedstawiono podstawową strukturę używania programu concurrent_unordered_multimap
. Ten przykład wstawia klucze znaków w zakresie ['a', 'i']. concurrent_unordered_multimap
umożliwia kluczowi posiadanie wielu wartości.
// 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 — Klasa
Klasa concurrency::concurrent_unordered_set jest ściśle podobna concurrent_unordered_map
do klasy, z tą różnicą, że zarządza wartościami zamiast par klucz i wartość. Klasa concurrent_unordered_set
nie udostępnia operator[]
ani at
metody .
W poniższym przykładzie przedstawiono podstawową strukturę używania programu concurrent_unordered_set
. W tym przykładzie wstawia wartości znaków w zakresie ['a', 'i']. Równoczesne wykonywanie wstawiania jest bezpieczne.
// 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 — Klasa
Klasa concurrency::concurrent_unordered_multiset jest ściśle podobna concurrent_unordered_set
do klasy, z tą różnicą, że zezwala na zduplikowane wartości. Różni się on również od concurrent_unordered_set
następujących sposobów:
Metoda concurrent_unordered_multiset::insert zwraca iterator zamiast
std::pair<iterator, bool>
.Klasa
concurrent_unordered_multiset
nie udostępniaoperator[]
aniat
metody .
W poniższym przykładzie przedstawiono podstawową strukturę używania programu concurrent_unordered_multiset
. W tym przykładzie wstawia wartości znaków w zakresie ['a', 'i']. concurrent_unordered_multiset
umożliwia wielokrotne wystąpienie wartości.
// 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 — Klasa
Klasa concurrency::combinable udostępnia magazyn wielokrotnego użytku, wątkowo-lokalny, który umożliwia wykonywanie precyzyjnych obliczeń, a następnie scalanie tych obliczeń w końcowy wynik. Obiekt można traktować combinable
jako zmienną redukcji.
Klasa jest przydatna combinable
, gdy masz zasób, który jest współużytkowany między kilkoma wątkami lub zadaniami. Klasa combinable
pomaga wyeliminować stan udostępniony, zapewniając dostęp do zasobów udostępnionych w sposób wolny od blokady. W związku z tym ta klasa stanowi alternatywę dla korzystania z mechanizmu synchronizacji, na przykład mutexu, do synchronizowania dostępu do udostępnionych danych z wielu wątków.
Metody i funkcje
W poniższej tabeli przedstawiono niektóre ważne metody combinable
klasy. Aby uzyskać więcej informacji na temat wszystkich combinable
metod klas, zobacz łączenie klasy.
Metoda | opis |
---|---|
local | Pobiera odwołanie do zmiennej lokalnej skojarzonej z bieżącym kontekstem wątku. |
Wyczyść | Usuwa wszystkie zmienne lokalne wątku combinable z obiektu. |
Połączyć combine_each |
Używa udostępnionej funkcji łączenia, aby wygenerować ostateczną wartość z zestawu wszystkich obliczeń lokalnych wątków. |
Klasa combinable
jest klasą szablonu sparametryzowaną w końcowym scalanym wyniku. W przypadku wywołania konstruktora T
domyślnego typ parametru szablonu musi mieć konstruktor domyślny i konstruktor kopiujący. T
Jeśli typ parametru szablonu nie ma konstruktora domyślnego, wywołaj przeciążoną wersję konstruktora, który przyjmuje funkcję inicjowania jako parametr.
Dodatkowe dane można przechowywać w combinable
obiekcie po wywołaniu metody łączenia lub combine_each . Metody i combine_each
można również wywołać combine
wiele razy. Jeśli w obiekcie nie zmienia się żadna wartość lokalna combinable
, combine
metody i combine_each
generują ten sam wynik za każdym razem, gdy są wywoływane.
Przykłady
Przykłady dotyczące korzystania z combinable
klasy można znaleźć w następujących tematach:
Instrukcje: korzystanie z wyników połączonych do poprawiania wydajności
Instrukcje: korzystanie z wyników połączonych w celu łączenia zestawów
[Top]
Tematy pokrewne
Instrukcje: korzystanie z kontenerów równoległych do zwiększania wydajności
Pokazuje, jak używać kontenerów równoległych do wydajnego przechowywania danych i uzyskiwania do nich dostępu równolegle.
Instrukcje: korzystanie z wyników połączonych do poprawiania wydajności
Pokazuje, jak za pomocą combinable
klasy wyeliminować stan udostępniony, a tym samym zwiększyć wydajność.
Instrukcje: korzystanie z wyników połączonych w celu łączenia zestawów
Pokazuje, jak używać combine
funkcji do scalania zestawów danych wątkowych lokalnych.
Biblioteka równoległych wzorców (PLL)
Opisuje PPL, który zapewnia imperatywnego modelu programowania, który promuje skalowalność i łatwość użycia do tworzenia współbieżnych aplikacji.
Odwołanie
concurrent_unordered_map, klasa
concurrent_unordered_multimap, klasa
concurrent_unordered_set, klasa
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla