Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A párhuzamos minták kódtára (PPL) számos tárolót és objektumot tartalmaz, amelyek szálbiztos hozzáférést biztosítanak az elemeikhez.
Az egyidejű tároló egyidejűleg biztonságos hozzáférést biztosít a legfontosabb műveletekhez. Itt az egyidejűség-biztonságos érték azt jelenti, hogy a mutató vagy az iterátor mindig érvényes. Ez nem garantálja az elemek inicializálását vagy egy adott bejárási sorrendet. Ezeknek a tárolóknak a funkciói a C++ standard kódtár által biztosítottakhoz hasonlóak. Az egyidejűség::concurrent_vector osztály például az std::vector osztályhoz hasonlít, azzal a kivétellel, hogy az concurrent_vector osztály lehetővé teszi az elemek párhuzamos hozzáfűzését. Egyidejű tárolókat akkor használjon, ha olyan párhuzamos kóddal rendelkezik, amely olvasási és írási hozzáférést is igényel ugyanahhoz a tárolóhoz.
Az egyidejű objektumokat a rendszer egyidejűleg osztja meg az összetevők között. Az egyidejű objektumok állapotát párhuzamosan kiszámoló folyamat ugyanazt az eredményt hozza létre, mint egy másik folyamat, amely ugyanazt az állapotot sorosan számítja ki. Az concurrency::combinable osztály az egyidejű objektum típusának egyik példája. Az combinable osztály lehetővé teszi a számítások párhuzamos végrehajtását, majd ezeket a számításokat egy végső eredményben egyesíti. Használjon egyidejű objektumokat, ha egyébként egy szinkronizálási mechanizmust, például egy mutexet használna a megosztott változóhoz vagy erőforráshoz való hozzáférés szinkronizálásához.
Szakaszok
Ez a témakör részletesen ismerteti a következő párhuzamos tárolókat és objektumokat.
Egyidejű tárolók:
Egyidejű objektumok:
concurrent_vector osztály
Az egyidejűség::concurrent_vector osztály egy szekvenciatároló-osztály, amely az std::vector osztályhoz hasonlóan lehetővé teszi az elemek véletlenszerű elérését. Az concurrent_vector osztály lehetővé teszi az egyidejűségmentes hozzáfűzési és elemhozzáférési műveleteket. A hozzáfűzési műveletek nem érvényteleníti a meglévő mutatókat vagy iterátorokat. Az iterátor-hozzáférés és a bejárási műveletek egyidejűleg is biztonságosak. Itt az egyidejűség-biztonságos érték azt jelenti, hogy a mutató vagy az iterátor mindig érvényes. Ez nem garantálja az elemek inicializálását vagy egy adott bejárási sorrendet.
Különbségek a concurrent_vector és a vektor között
Az concurrent_vector osztály nagyon hasonlít az osztályra vector . Az objektumon végzett hozzáfűzési, elemhozzáférési és iterátor-hozzáférési műveletek concurrent_vector összetettsége megegyezik az vector objektuméval. Az alábbi pontok bemutatják, hogy miben concurrent_vector különbözik a következőtől vector:
Az
concurrent_vectorobjektum hozzáfűzési, elemhozzáférési, iterátor-hozzáférési és iterátor-bejárási műveletei szálbiztosak.Az
concurrent_vectorobjektumhoz csak a végén adhat elemeket hozzá. Azconcurrent_vectorosztály nem adja meg a metódustinsert.A
concurrent_vectorobjektum nem használ áthelyezési szemantikát, amikor hozzáfűz valamit.Az
concurrent_vectorosztály nem biztosítja aerasevagy apop_backmetódusokat.vectorA tiszta metódussal az összes elemet eltávolíthatja egyconcurrent_vectorobjektumból.Az
concurrent_vectorosztály nem tárolja az elemeit egybefüggően a memóriában. Ezért nem használhatja azconcurrent_vectorosztályt minden olyan módon, amellyel tömböt használhat. Például egy típusnakvnevezettconcurrent_vectorváltozó esetében a kifejezés&v[0]+2nem definiált viselkedést eredményez.Az
concurrent_vectorosztály határozza meg a grow_by és grow_to_at_least metódusokat. Ezek a metódusok az átméretezési módszerhez hasonlítanak, azzal a kivételével, hogy egyidejűleg biztonságosak.Egy
concurrent_vectorobjektum nem helyezi át az elemeit, amikor hozzáfűzi vagy átméretezi. Ez lehetővé teszi, hogy a meglévő mutatók és iterátorok érvényesek maradjanak az egyidejű műveletek során.A futtatókörnyezet nem definiálja a
concurrent_vectortípusboolspeciális verzióját.
Concurrency-Safe műveletek
Minden metódus, amely hozzáfűzi vagy növeli egy concurrent_vector objektum méretét, vagy egy objektum egy eleméhez concurrent_vector fér hozzá, egyidejűségbiztos. Itt az egyidejűség-biztonságos érték azt jelenti, hogy a mutató vagy az iterátor mindig érvényes. Ez nem garantálja az elemek inicializálását vagy egy adott bejárási sorrendet. A szabály alól kivételt képez a resize metódus.
Az alábbi táblázat azokat a gyakori concurrent_vector metódusokat és operátorokat mutatja be, amelyek egyidejűleg biztonságosak.
A futtatókörnyezet által a C++ standard könyvtárral való kompatibilitást biztosító műveletek, például reserve, nem szálbiztosak. Az alábbi táblázat azokat a gyakori metódusokat és operátorokat mutatja be, amelyek nem egyidejűleg biztonságosak.
A meglévő elemek értékét módosító műveletek nem szálbiztosak. Egy szinkronizálási objektum, például egy reader_writer_lock objektum használatával szinkronizálhatja az egyidejű olvasási és írási műveleteket ugyanarra az adatelemre. A szinkronizálási objektumokról további információt a Szinkronizálási adatstruktúrák című témakörben talál.
Amikor átalakítja a meglévő kódot, ami vector-ot használ concurrent_vector-re, az egyidejű műveletek az alkalmazás viselkedésének megváltozását okozhatják. Vegyük például az alábbi programot, amely egyszerre két feladatot hajt végre egy concurrent_vector objektumon. Az első tevékenység további elemeket fűz hozzá egy concurrent_vector objektumhoz. A második tevékenység kiszámítja az összes elem összegét ugyanabban az objektumban.
// 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;
}
);
}
Bár a end metódus egyidejűleg biztonságos, a push_back metódus egyidejű hívása miatt a visszaadott end érték megváltozik. Az iterátor által bejárt elemek száma meghatározhatatlan. Ezért ez a program minden futtatáskor más eredményt tud eredményezni. Ha az elem típusa nem triviális, lehetséges, hogy versenyfeltétel létezhet a push_back és end hívások között. A end metódus visszaadhat egy lefoglalt, de nem teljesen inicializált elemet.
Kivételbiztonság
Ha egy növekedési vagy hozzárendelési művelet kivételt eredményez, az concurrent_vector objektum állapota érvénytelenné válik. Az érvénytelen állapotú objektumok viselkedése concurrent_vector nincs meghatározva, kivéve, ha másként van megadva. A destruktor azonban mindig felszabadítja az objektum által lefoglalt memóriát, még akkor is, ha az objektum érvénytelen állapotban van.
A vektorelemek Tadattípusának meg kell felelnie az alábbi követelményeknek. Ellenkező esetben az concurrent_vector osztály viselkedése nincs meghatározva.
A destruktort nem szabad dobni.
Ha az alapértelmezett vagy másolási konstruktor kivételt dob, a dekonstruktor nem deklarálható a
virtualkulcsszó használatával, és megfelelően kell működnie nulla inicializált memóriával.
[Felső]
concurrent_queue osztály
Az egyidejűség::concurrent_queue osztály, csakúgy, mint az std::queue osztály, lehetővé teszi annak elülső és hátsó elemeinek elérését. Az concurrent_queue osztály lehetővé teszi az egyidejű műveletek biztonságos végrehajtását a sorba állítás és a sorból kivétel során. Itt az egyidejűség-biztonságos érték azt jelenti, hogy a mutató vagy az iterátor mindig érvényes. Ez nem garantálja az elemek inicializálását vagy egy adott bejárási sorrendet. Az concurrent_queue osztály olyan iterátortámogatást is biztosít, amely nem egyidejűségbiztos.
Különbségek a concurrent_queue és a sor között
Az concurrent_queue osztály nagyon hasonlít az osztályra queue . Az alábbi pontok bemutatják, hogy miben concurrent_queue különbözik a következőtől queue:
Az
concurrent_queueobjektum enqueue és dequeue műveletei szálbiztosak.Az
concurrent_queueosztály olyan iterátortámogatást biztosít, amely nem egyidejűségbiztos.Az
concurrent_queueosztály nem biztosítja afrontvagy apopmetódusokat. Azconcurrent_queueosztály ezeket a metódusokat a try_pop metódus definiálásával helyettesíti.Az
concurrent_queueosztály nem adja meg a metódustback. Ezért nem hivatkozhat az üzenetsor végére.A
concurrent_queueosztály a metódus helyett asizemetódust biztosítja. Aunsafe_sizemetódus nem egyidejűségbiztos.
Concurrency-Safe műveletek
Minden olyan metódus, amely egy concurrent_queue objektumra hivatkozik vagy onnan leküldi azokat, az egyidejűség-biztonságos. Itt az egyidejűség-biztonságos érték azt jelenti, hogy a mutató vagy az iterátor mindig érvényes. Ez nem garantálja az elemek inicializálását vagy egy adott bejárási sorrendet.
Az alábbi táblázat azokat a gyakori concurrent_queue metódusokat és operátorokat mutatja be, amelyek egyidejűleg biztonságosak.
Bár a empty metódus egyidejűleg biztonságos, az egyidejű művelet miatt az üzenetsor növekedhet vagy zsugorodhat, mielőtt a empty metódus visszatér.
Az alábbi táblázat azokat a gyakori metódusokat és operátorokat mutatja be, amelyek nem egyidejűleg biztonságosak.
Iterator-támogatás
A concurrent_queue szolgáltatás olyan iterátorokat biztosít, amelyek nem egyidejűleg biztonságosak. Javasoljuk, hogy ezeket az iterátorokat csak hibakereséshez használja.
Az concurrent_queue iterátor csak előrefelé halad az elemek között. Az alábbi táblázat az egyes iterátorok által támogatott operátorokat mutatja be.
| Operátor | Leírás |
|---|---|
operator++ |
Tovább lép a sorban következő elemre. Ez az operátor azért van túlterhelve, hogy biztosítsa mind az előtti, mind az utáni növekmény szemantikáját. |
operator* |
Az aktuális elemre mutató hivatkozást kér le. |
operator-> |
Az aktuális elemre mutató mutatót kér le. |
[Felső]
concurrent_unordered_map osztály
Az egyidejűség::concurrent_unordered_map osztály egy asszociatív tárolóosztály, amely az std::unordered_map osztályhoz hasonlóan az std::pair<const Key, Ty> típusú elemek változó hosszúságú szekvenciáját vezérli. A rendezetlen térképeket szótárként tekintheti, amelyekhez kulcs- és értékpárokat adhat hozzá, vagy kulcs alapján kereshet értékeket. Ez az osztály akkor hasznos, ha több szálat vagy feladatot használ, amelyeknek egyidejűleg hozzá kell férnie egy megosztott tárolóhoz, be kell szúrnia vagy frissítenie kell azt.
Az alábbi példa a használat concurrent_unordered_mapalapvető struktúráját mutatja be. Ez a példa beszúrja a karakterkulcsokat az "a", "i" tartományba. Mivel a műveletek sorrendje meghatározatlan, az egyes kulcsok végső értéke szintén meghatározatlan. A beszúrások azonban biztonságosan elvégezhetők párhuzamosan.
// 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]
*/
Egy térkép és a csökkentési művelet párhuzamos végrehajtására szolgáló concurrent_unordered_map példa : Útmutató: Térkép és csökkentési műveletek párhuzamos végrehajtása.
A concurrent_unordered_map és a unordered_map közötti különbségek
Az concurrent_unordered_map osztály nagyon hasonlít az osztályra unordered_map . Az alábbi pontok bemutatják, hogy miben concurrent_unordered_map különbözik a következőtől unordered_map:
A
erase,bucket,bucket_count, ésbucket_sizemetódusokat rendreunsafe_erase,unsafe_bucket,unsafe_bucket_count, ésunsafe_bucket_sizenévvel illették. Azunsafe_elnevezési konvenció azt jelzi, hogy ezek a metódusok nem szálbiztosak. Az egyidejűség biztonságáról további információt azConcurrency-Safe műveletek című témakörben talál.A beszúrási műveletek nem érvénytelenítik a meglévő mutatókat vagy iterátorokat, és nem módosítják a térképen már létező elemek sorrendjét sem. A beszúrási és az átjárási műveletek egyidejűleg is előfordulhatnak.
concurrent_unordered_mapcsak a továbbítási iterációt támogatja.A beszúrás nem érvényteleníti vagy frissíti a visszaadott
equal_rangeiterátorokat. A beszúrás nem egyenlő elemeket fűzhet a tartomány végéhez. A kezdő iterátor egy egyenlőségelemre mutat.
A holtpont elkerülése érdekében a concurrent_unordered_map egyetlen metódusa sem tart fenn zárolást, amikor a memóriaelosztót, a kivonatfüggvényeket vagy más felhasználó által definiált kódot hív meg. Emellett gondoskodnia kell arról is, hogy a kivonatoló függvény mindig azonos értékhez azonos kulcsokat értékeljen ki. A legjobb kivonatfüggvények egységesen osztják el a kulcsokat a kivonat kódterületén.
Concurrency-Safe műveletek
Az concurrent_unordered_map osztály lehetővé teszi az egyidejűség-biztonságos beszúrási és elemhozzáférési műveleteket. A beszúrási műveletek nem érvényteleníti a meglévő mutatókat vagy iterátorokat. Az iterátor-hozzáférés és a bejárási műveletek egyidejűleg is biztonságosak. Itt az egyidejűség-biztonságos érték azt jelenti, hogy a mutató vagy az iterátor mindig érvényes. Ez nem garantálja az elemek inicializálását vagy egy adott bejárási sorrendet. Az alábbi táblázat azokat a gyakran használt concurrent_unordered_map metódusokat és operátorokat mutatja be, amelyek egyidejűleg biztonságosak.
Bár a count metódus biztonságosan hívható meg az egyidejűleg futó szálaktól, a különböző szálak eltérő eredményeket kaphatnak, ha egy új érték egyidejűleg van beszúrva a tárolóba.
Az alábbi táblázat azokat a gyakran használt módszereket és operátorokat mutatja be, amelyek nem biztonságosak a párhuzamos végrehajtás szempontjából.
Az említett metódusok mellett minden unsafe_-el kezdődő metódus sem szálbiztos.
[Felső]
concurrent_unordered_multimap osztály
Az egyidejűség::concurrent_unordered_multimap osztály nagyon hasonlít a(z) concurrent_unordered_map osztályra, azzal a kivétellel, hogy több érték is társítható ugyanahhoz a kulcshoz. A concurrent_unordered_map az alábbi módokon is különbözik:
A concurrent_unordered_multimap::insert metódus iterátort ad vissza
std::pair<iterator, bool>helyett.Az
concurrent_unordered_multimaposztály nem adja megoperator[]és nem is a metódustat.
Az alábbi példa a használat concurrent_unordered_multimapalapvető struktúráját mutatja be. Ez a példa beszúrja a karakterkulcsokat az "a", "i" tartományba.
concurrent_unordered_multimap lehetővé teszi, hogy egy kulcs több értékkel rendelkezzen.
// 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]
*/
[Felső]
concurrent_unordered_set osztály
Az egyidejűség::concurrent_unordered_set osztály nagyon hasonlít a concurrent_unordered_map osztályra, azzal a különbséggel, hogy kulcs- és értékpárok helyett csak értékeket kezel. Az concurrent_unordered_set osztály nem adja meg operator[] és nem is a metódust at .
Az alábbi példa a használat concurrent_unordered_setalapvető struktúráját mutatja be. Ez a példa karakterértékeket szúr be az "a", "i" tartományba. A beszúrások biztonságosan elvégezhetők párhuzamosan.
// 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]
*/
[Felső]
concurrent_unordered_multiset osztály
Az egyidejűség::concurrent_unordered_multiset osztály nagyon hasonlít az concurrent_unordered_set osztályra, kivéve, hogy duplikált értékeket tesz lehetővé. A concurrent_unordered_set az alábbi módokon is különbözik:
A concurrent_unordered_multiset::insert metódus egy iterátort ad vissza
std::pair<iterator, bool>helyett.Az
concurrent_unordered_multisetosztály nem adja megoperator[]és nem is a metódustat.
Az alábbi példa a használat concurrent_unordered_multisetalapvető struktúráját mutatja be. Ez a példa karakterértékeket szúr be az "a", "i" tartományba.
concurrent_unordered_multiset lehetővé teszi egy érték többszöri előfordulásá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]
*/
[Felső]
kombinálható osztály
Az egyidejűség::az kombinálható osztály újrafelhasználható, szálalapú tárolást biztosít, amely lehetővé teszi a részletes számítások elvégzését, majd a számítások végleges eredménybe való egyesítését. Az objektumok redukciós combinable változóként is felfoghatók.
Az combinable osztály akkor hasznos, ha több szál vagy tevékenység között megosztott erőforrással rendelkezik. Az combinable osztály segít megszüntetni a megosztott állapotot azáltal, hogy zárolásmentes hozzáférést biztosít a megosztott erőforrásokhoz. Ez az osztály tehát alternatívát nyújt a szinkronizálási mechanizmus, például egy mutex használata helyett, hogy szinkronizálja a több szálból származó megosztott adatokhoz való hozzáférést.
Metódusok és szolgáltatások
Az alábbi táblázat az osztály néhány fontos metódusát mutatja be combinable . Az összes osztálymódszerről további információt az combinablekombinálható osztály című témakörben talál.
| Metódus | Leírás |
|---|---|
| helyi | Az aktuális szálkörnyezethez társított helyi változóra mutató hivatkozást kér le. |
| töröl | Eltávolítja az összes szál-helyi változót az combinable objektumból. |
|
kombinál combine_each |
A megadott összevonási függvény használatával végső értéket hoz létre az összes szál-helyi számítás halmazából. |
Az combinable osztály egy sablonosztály, amely a végső egyesített eredményre van paraméterezve. Ha meghívja az alapértelmezett konstruktort, a sablonparaméter típusának T alapértelmezett konstruktorsal és másolatkonstruktorsal kell rendelkeznie. Ha a T sablonparaméter típusa nem rendelkezik alapértelmezett konstruktorsal, hívja meg a konstruktor túlterhelt verzióját, amely paraméterként inicializálási függvényt vesz igénybe.
Az combinable vagy combine_each metódus meghívása után további adatokat is tárolhat egy objektumban. A combine és combine_each metódusokat többször is meghívhatja. Ha egy combinable objektumban nem változik helyi érték, a metódusok és combine a combine_each metódusok minden alkalommal ugyanazt az eredményt eredményezik, amikor meghívják őket.
Példák
Az osztály használatára combinable vonatkozó példákért tekintse meg az alábbi témaköröket:
[Felső]
Kapcsolódó témakörök
Útmutató: Párhuzamos tárolók használata a hatékonyság növeléséhez
Bemutatja, hogyan használható párhuzamos tárolók az adatok párhuzamos tárolására és elérésére.
Útmutató: Kombinálható eszközök használata a teljesítmény javítása érdekében
Bemutatja, hogyan használható az osztály a combinable megosztott állapot megszüntetésére, és ezáltal a teljesítmény javítására.
Útmutató: Kombinálható készletek kombinálása
Bemutatja, hogyan lehet függvényt combine használni a szál-helyi adatkészletek egyesítése érdekében.
Párhuzamos minták kódtára (PPL)
Ismerteti a PPL-t, amely egy imperatív programozási modellt biztosít, amely elősegíti a skálázhatóságot és a könnyű használatot az egyidejű alkalmazások fejlesztéséhez.
Referenciák
concurrent_unordered_map osztály
concurrent_unordered_multimap Osztály
concurrent_unordered_set osztály