Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Knihovna PPL (Parallel Patterns Library) obsahuje několik kontejnerů a objektů, které poskytují přístup k prvkům bezpečným pro přístup z více vláken.
Souběžný kontejner poskytuje souběžný přístup k nejdůležitějším operacím bezpečným pro souběžnost. V této chvíli souběžnost znamená, že ukazatele nebo iterátory jsou vždy platné. Nejedná se o záruku inicializace prvků ani konkrétního pořadí procházení. Funkce těchto kontejnerů se podobají funkcím, které poskytuje standardní knihovna jazyka C++. Například souběžnost::concurrent_vector třída se podobá třídě std::vector s tím rozdílem, že concurrent_vector třída umožňuje připojit prvky paralelně. Souběžné kontejnery používejte, pokud máte paralelní kód, který vyžaduje přístup ke stejnému kontejneru pro čtení i zápis.
Souběžný objekt se sdílí souběžně mezi komponentami. Proces, který paralelně vypočítá stav souběžného objektu, vytvoří stejný výsledek jako jiný proces, který vypočítá stejný stav sériově.
Concurrency::combinable třída je jedním z příkladů souběžného typu objektu. Třída combinable umožňuje provádět výpočty paralelně a pak tyto výpočty zkombinovat do konečného výsledku. Souběžné objekty použijte, pokud byste jinak použili synchronizační mechanismus, například mutex, k synchronizaci přístupu ke sdílené proměnné nebo prostředku.
Oddíly
Toto téma podrobně popisuje následující paralelní kontejnery a objekty.
Souběžné kontejnery:
Souběžné objekty:
concurrent_vector – třída
Concurrency::concurrent_vector třída je třída kontejneru sekvence, která stejně jako std::vector třída umožňuje náhodný přístup k jeho prvkům. Třída concurrent_vector umožňuje souběžnost-bezpečné připojení a přístup k prvkům operace. Operace připojení zneplatní existující ukazatele ani iterátory. Přístup iterátoru a operace procházení jsou také souběžné. V této chvíli souběžnost znamená, že ukazatele nebo iterátory jsou vždy platné. Nejedná se o záruku inicializace prvků ani konkrétního pořadí procházení.
Rozdíly mezi concurrent_vector a vektorem
Třída concurrent_vector se velmi podobá vector třídě. Složitost operací připojení, přístupu k prvkům a iterátoru u objektu concurrent_vector jsou stejné jako u objektu vector . Následující body ukazují, kde concurrent_vector se liší od vector:
Operace připojení, přístupu k prvkům, přístupu iterátoru a procházení iterátoru u objektu
concurrent_vectorjsou bezpečné pro souběžnost.Prvky lze přidat pouze na konec objektu
concurrent_vector. Třídaconcurrent_vectorneposkytuje metoduinsert.Objekt
concurrent_vectorpři připojení k objektu nepoužívá sémantiku přesunutí.Třída
concurrent_vectorneposkytujeeraseanipop_backmetody. Stejně jako uvector, použijte jasnou metodu k odebrání všech prvků z objektuconcurrent_vector.Třída
concurrent_vectorneukládá své prvky souvisle do paměti. Proto nelze třídu použítconcurrent_vectorvšemi způsoby, jak lze použít pole. Například pro proměnnou s názvemvtypuconcurrent_vectorvýraz&v[0]+2vytváří nedefinované chování.Třída
concurrent_vectordefinuje grow_by a grow_to_at_least metody. Tyto metody se podobají metodě změny velikosti , s tím rozdílem, že jsou bezpečné pro souběžnost.Objekt
concurrent_vectornepřemístí jeho prvky, když k němu připojíte nebo změníte jeho velikost. To umožňuje, aby stávající ukazatele a iterátory zůstaly platné během souběžných operací.Modul runtime nedefinuje specializovanou verzi
concurrent_vectorpro typbool.
Operace bezpečné souběžnosti
Všechny metody, které připojují nebo zvětšují velikost objektu concurrent_vector nebo přistupují k prvku v objektu concurrent_vector , jsou bezpečné pro souběžnost. V této chvíli souběžnost znamená, že ukazatele nebo iterátory jsou vždy platné. Nejedná se o záruku inicializace prvků ani konkrétního pořadí procházení. Výjimkou tohoto pravidla je resize metoda.
Následující tabulka uvádí běžné concurrent_vector metody a operátory, které jsou bezpečné pro souběžnost.
Operace, které modul runtime zajišťuje kompatibilitu se standardní knihovnou C++, reservenapříklad , nejsou bezpečné pro souběžnost. Následující tabulka uvádí běžné metody a operátory, které nejsou bezpečné pro souběžnost.
Operace, které upravují hodnotu existujících prvků, nejsou bezpečné pro souběžnost. Pomocí synchronizačního objektu , jako je například objekt reader_writer_lock , můžete synchronizovat souběžné operace čtení a zápisu do stejného datového prvku. Další informace o synchronizačních objektech naleznete v tématu Synchronizační datové struktury.
Při převodu existujícího kódu, který se používá vector k použití concurrent_vector, můžou souběžné operace způsobit změnu chování aplikace. Představte si například následující program, který současně provádí dva úkoly na objektu concurrent_vector . První úkol připojí k objektu concurrent_vector další prvky. Druhý úkol vypočítá součet všech prvků ve stejném objektu.
// 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 I když je metoda souběžná a bezpečná, souběžné volání metody push_back způsobí změnu hodnoty vrácené end změnou. Počet prvků, které iterátor prochází, je neurčitý. Proto může tento program při každém spuštění vytvořit jiný výsledek. Pokud typ prvku není triviální, je možné, že mezi voláními a push_back voláním existuje end podmínka časování. Metoda end může vrátit prvek, který je přidělen, ale není plně inicializován.
Bezpečnost výjimek
Pokud operace růstu nebo přiřazení vyvolá výjimku, stav objektu concurrent_vector bude neplatný. Chování objektu concurrent_vector , který je v neplatném stavu, není definován, pokud není uvedeno jinak. Destruktor však vždy uvolní paměť, kterou objekt přidělí, i když je objekt v neplatném stavu.
Datový typ vektorových prvků musí Tsplňovat následující požadavky. V opačném případě je chování concurrent_vector třídy nedefinováno.
Destruktor nesmí vyvolat.
Pokud dojde k vyvolání výchozího konstruktoru nebo konstruktoru kopírování, nesmí být destruktor deklarován pomocí klíčového
virtualslova a musí správně fungovat s nulou inicializovanou pamětí.
[Nahoře]
concurrent_queue – třída
Třída concurrency::concurrent_queue , stejně jako třída std::queue , umožňuje přístup k jeho předním a zadním prvkům. Třída concurrent_queue umožňuje souběžnost-safe enqueue a dequeue operace. V této chvíli souběžnost znamená, že ukazatele nebo iterátory jsou vždy platné. Nejedná se o záruku inicializace prvků ani konkrétního pořadí procházení. Třída concurrent_queue také poskytuje podporu iterátoru, která není bezpečná pro souběžnost.
Rozdíly mezi concurrent_queue a frontou
Třída concurrent_queue se velmi podobá queue třídě. Následující body ukazují, kde concurrent_queue se liší od queue:
Operace fronty a dequeue u
concurrent_queueobjektu jsou bezpečné pro souběžnost.Třída
concurrent_queueposkytuje podporu iterátoru, která není bezpečná pro souběžnost.Třída
concurrent_queueneposkytujefrontanipopmetody. Třídaconcurrent_queuenahrazuje tyto metody definováním try_pop metody.Třída
concurrent_queueneposkytuje metoduback. Proto nelze odkazovat na konec fronty.Třída
concurrent_queueposkytuje unsafe_size metodusizemísto metody. Metodaunsafe_sizenení bezpečná pro souběžnost.
Operace bezpečné souběžnosti
Všechny metody, které zařadí do fronty nebo odřazení z objektu concurrent_queue , jsou concurrency-safe. V této chvíli souběžnost znamená, že ukazatele nebo iterátory jsou vždy platné. Nejedná se o záruku inicializace prvků ani konkrétního pořadí procházení.
Následující tabulka uvádí běžné concurrent_queue metody a operátory, které jsou bezpečné pro souběžnost.
empty I když je metoda bezpečná pro souběžnost, souběžná operace může způsobit zvětšení nebo zmenšení fronty před vrácením empty metody.
Následující tabulka uvádí běžné metody a operátory, které nejsou bezpečné pro souběžnost.
Podpora iterátoru
Poskytuje concurrent_queue iterátory, které nejsou bezpečné pro souběžnost. Tyto iterátory doporučujeme používat pouze pro ladění.
concurrent_queue Iterátor prochází prvky pouze směrem dopředu. Následující tabulka ukazuje operátory, které každý iterátor podporuje.
| Operátor | Popis |
|---|---|
operator++ |
Přejde na další položku ve frontě. Tento operátor je přetížen tak, aby poskytoval sémantiku před přírůstkem i po přírůstku. |
operator* |
Načte odkaz na aktuální položku. |
operator-> |
Načte ukazatel na aktuální položku. |
[Nahoře]
concurrent_unordered_map – třída
Concurrency::concurrent_unordered_map třída je asociativní třída kontejneru, která stejně jako std::unordered_map třída řídí různou délku prvků typu std::p air<const Key, Ty>. Neuspořádanou mapu si můžete představit jako slovník, do kterého můžete přidat pár klíč a hodnota nebo vyhledat hodnotu podle klíče. Tato třída je užitečná, pokud máte více vláken nebo úloh, které musí současně přistupovat ke sdílenému kontejneru, vložit do něj nebo je aktualizovat.
Následující příklad ukazuje základní strukturu pro použití concurrent_unordered_map. Tento příklad vloží klíče znaků do oblasti ['a', 'i']. Vzhledem k tomu, že pořadí operací není definováno, je také neurčitá konečná hodnota každého klíče. Je však bezpečné provádět vložení paralelně.
// 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]
*/
Příklad, který používá concurrent_unordered_map k paralelnímu provádění operace mapování a redukce, najdete v tématu Postupy: Provádění operací mapování a redukce paralelně.
Rozdíly mezi concurrent_unordered_map a unordered_map
Třída concurrent_unordered_map se velmi podobá unordered_map třídě. Následující body ukazují, kde concurrent_unordered_map se liší od unordered_map:
,
erase,bucketbucket_countabucket_sizemetody jsou pojmenoványunsafe_erase,unsafe_bucketunsafe_bucket_count, aunsafe_bucket_size, v uvedeném pořadí. Konvenceunsafe_vytváření názvů značí, že tyto metody nejsou bezpečné pro souběžnost. Další informace o bezpečnosti souběžnosti naleznete v tématu Souběžnost-Bezpečné operace.Operace vložení neoznačují stávající ukazatele ani iterátory, ani nemění pořadí položek, které již v mapě existují. Operace vložení a procházení můžou probíhat souběžně.
concurrent_unordered_mappodporuje pouze iteraci vpřed.Vložení zneplatní ani neaktualizuje iterátory, které vrací
equal_range. Vložení může připojit nerovné položky na konec rozsahu. Počáteční iterátor odkazuje na stejnou položku.
Aby se zabránilo vzájemnému zablokování, není při volání alokátoru paměti, funkcí hash nebo jiného uživatelem definovaného concurrent_unordered_map kódu žádná metoda uzamčení. Také je nutné zajistit, aby funkce hash vždy vyhodnocuje stejné klíče se stejnou hodnotou. Nejlepší hashovací funkce distribuují klíče rovnoměrně napříč prostorem kódu hash.
Operace bezpečné souběžnosti
Třída concurrent_unordered_map umožňuje operace souběžnosti bezpečné vložení a přístupu k prvkům. Operace vložení zneplatní existující ukazatele ani iterátory. Přístup iterátoru a operace procházení jsou také souběžné. V této chvíli souběžnost znamená, že ukazatele nebo iterátory jsou vždy platné. Nejedná se o záruku inicializace prvků ani konkrétního pořadí procházení. Následující tabulka uvádí běžně používané concurrent_unordered_map metody a operátory, které jsou bezpečné pro souběžnost.
I když lze metodu count volat bezpečně z souběžně spuštěných vláken, různá vlákna mohou přijímat různé výsledky, pokud je do kontejneru současně vložena nová hodnota.
Následující tabulka uvádí běžně používané metody a operátory, které nejsou bezpečné pro souběžnost.
Kromě těchto metod není žádná metoda, která začíná unsafe_ na souběžnosti, bezpečná.
[Nahoře]
concurrent_unordered_multimap – třída
Souběžnost ::concurrent_unordered_multimap třída úzce připomíná concurrent_unordered_map třídu s tím rozdílem, že umožňuje mapování více hodnot na stejný klíč. Liší se také od concurrent_unordered_map následujících způsobů:
Metoda concurrent_unordered_multimap::insert vrátí iterátor místo
std::pair<iterator, bool>.Třída
concurrent_unordered_multimapneposkytujeoperator[]ani metoduat.
Následující příklad ukazuje základní strukturu pro použití concurrent_unordered_multimap. Tento příklad vloží klíče znaků do oblasti ['a', 'i'].
concurrent_unordered_multimap umožňuje, aby klíč měl více hodnot.
// 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]
*/
[Nahoře]
concurrent_unordered_set – třída
Souběžnost ::concurrent_unordered_set třída úzce připomíná concurrent_unordered_map třídu s tím rozdílem, že spravuje hodnoty místo párů klíč a hodnota. Třída concurrent_unordered_set neposkytuje operator[] ani metodu at .
Následující příklad ukazuje základní strukturu pro použití concurrent_unordered_set. Tento příklad vloží hodnoty znaků do oblasti ['a', 'i']. Vložení je bezpečné provádět paralelně.
// 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]
*/
[Nahoře]
concurrent_unordered_multiset – třída
Souběžnost ::concurrent_unordered_multiset třída úzce připomíná concurrent_unordered_set třídu s tím rozdílem, že umožňuje duplicitní hodnoty. Liší se také od concurrent_unordered_set následujících způsobů:
Metoda concurrent_unordered_multiset::insert vrátí iterátor místo
std::pair<iterator, bool>.Třída
concurrent_unordered_multisetneposkytujeoperator[]ani metoduat.
Následující příklad ukazuje základní strukturu pro použití concurrent_unordered_multiset. Tento příklad vloží hodnoty znaků do oblasti ['a', 'i'].
concurrent_unordered_multiset umožňuje, aby se hodnota vyskytla vícekrát.
// 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]
*/
[Nahoře]
combinable – třída
Concurrency::combinable třída poskytuje opakovaně použitelné úložiště s vlákny místní úložiště, které umožňuje provádět jemně odstupňované výpočty a pak tyto výpočty sloučit do konečného výsledku. Objekt si můžete představit combinable jako proměnnou redukce.
Třída combinable je užitečná, pokud máte prostředek sdílený mezi několika vlákny nebo úkoly. Třída combinable pomáhá eliminovat sdílený stav tím, že poskytuje přístup ke sdíleným prostředkům způsobem bez uzamčení. Proto tato třída poskytuje alternativu k použití synchronizačního mechanismu, například mutex, k synchronizaci přístupu ke sdíleným datům z více vláken.
Metody a funkce
Následující tabulka uvádí některé z důležitých combinable metod třídy. Další informace o všech combinable metodách třídy naleznete v tématu combinable Class.
| metoda | Popis |
|---|---|
| místní | Načte odkaz na místní proměnnou přidruženou k aktuálnímu kontextu vlákna. |
| jasný | Odebere z objektu všechny proměnné místní podprocesu combinable . |
|
kombinovat combine_each |
Použije zadanou kombinační funkci k vygenerování konečné hodnoty ze sady všech výpočtů místních vláken. |
Třída combinable je třída šablony, která je parametrizována u konečného sloučeného výsledku. Pokud voláte výchozí konstruktor, T typ parametru šablony musí mít výchozí konstruktor a konstruktor kopírování.
T Pokud typ parametru šablony nemá výchozí konstruktor, zavolejte přetíženou verzi konstruktoru, který přebírá inicializační funkci jako jeho parametr.
Po volání kombinační nebo combinable metod můžete do objektu uložit další data. Metody a combine metody můžete také volat combine_each vícekrát. Pokud se v objektu combinable nezmění žádná místní hodnota, combine tyto metody combine_each při každém zavolání vytvoří stejný výsledek.
Příklady
Příklady použití combinable třídy najdete v následujících tématech:
[Nahoře]
Související témata
Postupy: Použití paralelních kontejnerů ke zvýšení účinnosti
Ukazuje, jak používat paralelní kontejnery k efektivnímu ukládání a přístupu k datům paralelně.
Postupy: Použití objektu combinable ke zlepšení výkonu
Ukazuje, jak pomocí combinable třídy eliminovat sdílený stav, a tím zlepšit výkon.
Postupy: Použití objektu combinable ke slučování množin
Ukazuje, jak pomocí combine funkce sloučit místní sady dat z více vláken.
Knihovna PPL (Parallel Patterns Library)
Popisuje PPL, který poskytuje imperativní programovací model, který podporuje škálovatelnost a snadné použití pro vývoj souběžných aplikací.
Odkazy
concurrent_unordered_map – třída
concurrent_unordered_multimap – třída
concurrent_unordered_set – třída