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.
Az Ügynökkódtár számos olyan üzenetblokktípust biztosít, amelyek lehetővé teszik az üzenetek szálbiztos módon történő propagálását az alkalmazásösszetevők között. Ezeket az üzenetblokktípusokat gyakran használják a különböző üzenetátadási rutinokkal, például egyidejűség::küldés, egyidejűség::asend, egyidejűség::fogadás és egyidejűség::try_receive. Az ügynökkódtár által meghatározott üzenetátadási rutinokról további információt az Üzenetátadási függvények című témakörben talál.
Szakaszok
Ez a témakör a következő szakaszokat tartalmazza:
Források és célok
A források és a célok az üzenetátadás két fontos résztvevője. A forrás az üzeneteket küldő kommunikációs végpontra hivatkozik. A cél az üzeneteket fogadó kommunikációs végpontra vonatkozik. A forrást olyan végpontként tekintheti, amelyből olvas, a cél pedig olyan végpont, amelybe ír. Az alkalmazások egymáshoz kötik a forrásokat és a célokat az üzenetkezelő hálózatok létrehozásához.
Az Agents Library két absztrakt osztályt használ a források és célpontok ábrázolására: concurrency::ISource és concurrency::ITarget. Azok az üzenetblokktípusok, amelyek forrásként működnek, a ISource-ból származnak; azok az üzenetblokktípusok, amelyek célként működnek, a ITarget-ből származnak. A forrásként és célként működő üzenetblokk-típusok mind ISourceITargeta kettőből származnak.
[Felső]
Üzenet propagálása
Az üzenetek propagálása egy üzenet egyik összetevőről a másikra való küldésének művelete. Amikor egy üzenetet felkínálnak egy üzenetblokknak, az elfogadhatja, elutasíthatja, vagy elhalaszthatja azt. Minden üzenetblokktípus különböző módon tárolja és továbbítja az üzeneteket. Az osztály például unbounded_buffer korlátlan számú üzenetet tárol, az overwrite_buffer osztály egyszerre egyetlen üzenetet tárol, a transzformátorosztály pedig az egyes üzenetek módosított verzióját tárolja. Ezeket az üzenetblokktípusokat a dokumentum későbbi részében részletesebben ismertetjük.
Ha egy üzenetblokk elfogad egy üzenetet, az opcionálisan elvégezheti a munkát, és ha az üzenetblokk egy forrás, adja át az üzenetet a hálózat egy másik tagjának. Az üzenetblokkok szűrőfüggvények használatával elutasíthatják azokat az üzeneteket, amelyeket nem szeretne fogadni. A szűrőkről a témakör későbbi részében, az Üzenetszűrés című szakaszban olvashat bővebben. Az üzeneteket elhalasztó üzenetblokkok később lefoglalhatják és felhasználhatják azt. Az üzenetfoglalásról a témakör későbbi, Üzenetfoglalás című szakaszában olvashat bővebben.
Az Ügynökkódtár lehetővé teszi az üzenetblokkok aszinkron vagy szinkron továbbítását. Ha egy üzenetet szinkron módon ad át egy üzenetblokknak, például a send függvény használatával, a futtatókörnyezet addig blokkolja az aktuális környezetet, amíg a célblokk el nem fogadja vagy elutasítja az üzenetet. Ha egy üzenetet aszinkron módon ad át egy üzenetblokknak, például a asend függvény használatával, a futtatókörnyezet felajánlja az üzenetet a célnak, és ha a cél elfogadja az üzenetet, a futtatókörnyezet ütemez egy aszinkron feladatot, amely propagálja az üzenetet a fogadónak. A futtatókörnyezet egyszerű feladatokat használ az üzenetek együttműködési propagálására. Az egyszerűsített feladatokról további információt a Feladatütemezőben talál.
Az alkalmazások egymáshoz kötik a forrásokat és a célokat az üzenetkezelő hálózatok létrehozásához. Általában összekapcsolja a hálózatot, és meghívja send vagy asend, hogy átadja az adatokat a hálózatnak. Ha egy forrásüzenetblokkot egy célhoz szeretne csatlakoztatni, hívja meg a concurrency::ISource::link_target metódust. Ha le szeretne választani egy forrást egy célról, hívja meg a concurrency::ISource::unlink_target metódust. Ha le szeretné választani a forrásblokkot az összes célblokkról, hívja meg a concurrency::ISource::unlink_targets metódust. Ha az egyik előre definiált üzenetblokk-típus elhagyja a hatókört, vagy megsemmisül, az automatikusan leválasztja magát a célblokkokról. Egyes üzenetblokktípusok korlátozzák az általuk írható célok maximális számát. Az alábbi szakasz az előre definiált üzenetblokktípusokra vonatkozó korlátozásokat ismerteti.
[Felső]
Az üzenetblokktípusok áttekintése
Az alábbi táblázat röviden ismerteti a fontos üzenetblokk-típusok szerepét.
unbounded_buffer
Üzenetek várólistáját tárolja.
overwrite_buffer
Egyetlen üzenetet tárol, amely többször is írható és olvasható.
egyszeri hozzárendelés
Egyetlen, egyszerre írható és többször olvasható üzenetet tárol.
hívás
Akkor végez munkát, amikor üzenetet kap.
transzformátor
Akkor végez munkát, amikor adatokat fogad, és elküldi a munka eredményét egy másik célblokknak. Az transformer osztály különböző bemeneti és kimeneti típusokra képes.
választás
Kiválasztja az első elérhető üzenetet egy forráskészletből.
illesztés és többtípusos illesztés
Várjon, amíg az összes üzenet egy forráskészletből érkezik, majd egyesítse az üzeneteket egy üzenetben egy másik üzenetblokkhoz.
időzítő
Rendszeres időközönként üzenetet küld egy célblokknak.
Ezek az üzenetblokk-típusok különböző jellemzőkkel rendelkeznek, amelyek hasznossá teszik őket különböző helyzetekben. Néhány jellemző:
Propagálás típusa: Az üzenetblokk adatforrásként, adat fogadóként vagy mindkettőként működik-e.
Üzenetrendezés: Az üzenetblokk megtartja-e az üzenetek küldésének vagy fogadásának eredeti sorrendjét. Minden előre definiált üzenetblokktípus megtartja az üzenetek küldésének vagy fogadásának eredeti sorrendjét.
Forrásszám: Az üzenetblokk által olvasható források maximális száma.
Célszám: Az üzenetblokkba írható célok maximális száma.
Az alábbi táblázat bemutatja, hogyan viszonyulnak ezek a jellemzők a különböző üzenetblokktípusokhoz.
| Üzenetblokk típusa | Propagálás típusa (forrás, cél vagy mindkettő) | Üzenetsorrendezés (Rendezett vagy Rendezetlen) | Forrásszám | Célszám |
|---|---|---|---|---|
unbounded_buffer |
Mindkettő | Megrendelve | Korlátos | Korlátos |
overwrite_buffer |
Mindkettő | Megrendelve | Korlátos | Korlátos |
single_assignment |
Mindkettő | Megrendelve | Korlátos | Korlátos |
call |
Cél | Megrendelve | Korlátos | Nem alkalmazható |
transformer |
Mindkettő | Megrendelve | Korlátos | 1 |
choice |
Mindkettő | Megrendelve | 10 | 1 |
join |
Mindkettő | Megrendelve | Korlátos | 1 |
multitype_join |
Mindkettő | Megrendelve | 10 | 1 |
timer |
Forrás | Nem alkalmazható | Nem alkalmazható | 1 |
Az alábbi szakaszok részletesebben ismertetik az üzenetblokkok típusait.
[Felső]
unbounded_buffer osztály
Az concurrency::unbounded_buffer osztály általános célú aszinkron üzenetküldési struktúrát jelöl. Ez az osztály egy elsőként be, elsőként ki (FIFO) üzenetsort tárol azokból az üzenetekből, amelyek több forrásból írhatók, vagy több cél által olvashatók. Amikor egy cél üzenetet kap egy unbounded_buffer objektumtól, az üzenet el lesz távolítva az üzenetsorból. Ezért bár egy unbounded_buffer objektum több célt is tartalmazhat, minden egyes üzenetet csak egy cél fog megkapni. Az unbounded_buffer osztály akkor hasznos, ha több üzenetet szeretne átadni egy másik összetevőnek, és az összetevőnek minden egyes üzenetet fogadnia kell.
példa
Az alábbi példa bemutatja, hogyan lehet az unbounded_buffer osztály alapvető struktúrájával dolgozni. Ez a példa három értéket küld egy unbounded_buffer objektumnak, majd visszaolvassa ezeket az értékeket ugyanabból az objektumból.
// unbounded_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create an unbounded_buffer object that works with
// int data.
unbounded_buffer<int> items;
// Send a few items to the unbounded_buffer object.
send(items, 33);
send(items, 44);
send(items, 55);
// Read the items from the unbounded_buffer object and print
// them to the console.
wcout << receive(items) << endl;
wcout << receive(items) << endl;
wcout << receive(items) << endl;
}
Ez a példa a következő kimenetet hozza létre:
334455
Az unbounded_buffer osztály használatát bemutató teljes példáért lásd Útmutató: Különböző Producer-Consumer minták megvalósítása.
[Felső]
overwrite_buffer osztály
Az egyidejűség::overwrite_buffer osztály hasonlít a unbounded_buffer osztályra, azzal a kivétellel, hogy egy overwrite_buffer objektum csak egy üzenetet tárol. Ezenkívül ha egy cél egy objektumtól overwrite_buffer kap üzenetet, az üzenet nem lesz eltávolítva a pufferből. Ezért több cél kap egy másolatot az üzenetről.
Az overwrite_buffer osztály akkor hasznos, ha több üzenetet szeretne átadni egy másik összetevőnek, de az összetevőnek csak a legújabb értékre van szüksége. Ez az osztály akkor is hasznos, ha több összetevőnek szeretne üzenetet küldeni.
példa
Az alábbi példa bemutatja, hogyan lehet az overwrite_buffer osztály alapvető struktúrájával dolgozni. Ez a példa három értéket küld egy overwrite _buffer objektumnak, majd háromszor olvassa be ugyanabból az objektumból az aktuális értéket. Ez a példa az osztály példájához unbounded_buffer hasonló. Az overwrite_buffer osztály azonban csak egy üzenetet tárol. Emellett a futtatókörnyezet nem távolítja el az üzenetet egy overwrite_buffer objektumból az olvasás után.
// overwrite_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create an overwrite_buffer object that works with
// int data.
overwrite_buffer<int> item;
// Send a few items to the overwrite_buffer object.
send(item, 33);
send(item, 44);
send(item, 55);
// Read the current item from the overwrite_buffer object and print
// it to the console three times.
wcout << receive(item) << endl;
wcout << receive(item) << endl;
wcout << receive(item) << endl;
}
Ez a példa a következő kimenetet hozza létre:
555555
Az overwrite_buffer osztály használatát bemutató teljes példáért lásd Útmutató: Különböző Producer-Consumer minták megvalósítása.
[Felső]
egyszeres hozzárendelés osztály
Az egyidejűség::single_assignment osztály hasonlít a overwrite_buffer osztályra, azzal a kivétellel, hogy egy single_assignment objektum csak egyszer írható. Az osztályhoz overwrite_buffer hasonlóan, amikor egy cél egy objektumtól single_assignment kap üzenetet, a rendszer nem távolítja el az üzenetet az objektumból. Ezért több cél kap egy másolatot az üzenetről. Az single_assignment osztály akkor hasznos, ha egy üzenetet több összetevőnek szeretne közvetíteni.
példa
Az alábbi példa bemutatja, hogyan lehet az single_assignment osztály alapvető struktúrájával dolgozni. Ez a példa három értéket küld egy single_assignment objektumnak, majd háromszor olvassa be ugyanabból az objektumból az aktuális értéket. Ez a példa az osztály példájához overwrite_buffer hasonló. Bár mind az osztályok, mind az overwrite_buffersingle_assignment osztályok egyetlen üzenetet tárolnak, az single_assignment osztály csak egyszer írható.
// single_assignment-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create an single_assignment object that works with
// int data.
single_assignment<int> item;
// Send a few items to the single_assignment object.
send(item, 33);
send(item, 44);
send(item, 55);
// Read the current item from the single_assignment object and print
// it to the console three times.
wcout << receive(item) << endl;
wcout << receive(item) << endl;
wcout << receive(item) << endl;
}
Ez a példa a következő kimenetet hozza létre:
333333
A single_assignment osztály használatát bemutató teljes példáért lásd Útmutató: Futures szerződések implementálása.
[Felső]
osztály meghívása
Az párhuzamosság::call osztály egy üzenetfogadóként működik, amely végrehajt egy munkafüggvényt, amikor adatokat kap. Ez a munkafüggvény lehet lambda kifejezés, függvényobjektum vagy függvénymutató. Az call objektumok másképp viselkednek, mint a szokásos függvényhívások, mivel párhuzamosan működnek más olyan összetevőkkel, amelyek üzeneteket küldenek neki. Ha egy call objektum akkor végez munkát, amikor üzenetet kap, hozzáadja az üzenetet egy üzenetsorhoz. Minden call objektum abban a sorrendben dolgozza fel az üzenetsorba helyezett üzeneteket, amelyben azok érkeznek.
példa
Az alábbi példa bemutatja, hogyan lehet az call osztály alapvető struktúrájával dolgozni. Ez a példa létrehoz egy call objektumot, amely kinyomtatja a konzolnak kapott értékeket. A példa ezután három értéket küld az call objektumnak. Mivel az call objektum egy külön szálon dolgozza fel az üzeneteket, ez a példa egy számlálóváltozót és egy eseményobjektumot is használ annak biztosítására, hogy az call objektum az összes üzenetet feldolgozza a wmain függvény visszatérése előtt.
// call-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// An event that is set when the call object receives all values.
event received_all;
// Counts the
long receive_count = 0L;
long max_receive_count = 3L;
// Create an call object that works with int data.
call<int> target([&received_all,&receive_count,max_receive_count](int n) {
// Print the value that the call object receives to the console.
wcout << n << endl;
// Set the event when all messages have been processed.
if (++receive_count == max_receive_count)
received_all.set();
});
// Send a few items to the call object.
send(target, 33);
send(target, 44);
send(target, 55);
// Wait for the call object to process all items.
received_all.wait();
}
Ez a példa a következő kimenetet hozza létre:
334455
Az call osztály használatát teljes példával szemléltetve, lásd: Hogyan biztosítsunk munkafüggvényeket a hívási és átalakítási osztályoknak.
[Felső]
transformer class
Az egyidejűség::transformer osztály üzenet fogadóként és üzenetküldőként is működik. Az transformer osztály azért hasonlít az call osztályra, mert felhasználó által definiált munkafüggvényt hajt végre, amikor adatokat fogad. Az osztály azonban a transformer munkafüggvény eredményét is elküldi a fogadó objektumoknak. Az objektumhoz call hasonlóan az transformer objektumok is párhuzamosan viselkednek az üzeneteket küldő más összetevőkkel. Ha egy transformer objektum akkor végez munkát, amikor üzenetet kap, hozzáadja az üzenetet egy üzenetsorhoz. Minden transformer objektum az üzenetsorba helyezett üzeneteket a beérkezés sorrendjében dolgozza fel.
Az transformer osztály egy célnak küldi el az üzenetét. Ha a _PTarget paramétert a konstruktorban NULL értékre állítja be, később megadhatja a célt a concurrency::link_target metódus meghívásával.
Az Ügynökök tára által biztosított összes többi aszinkron üzenetblokktípustól eltérően az transformer osztály különböző bemeneti és kimeneti típusokon is működhet. Az adatok egyik típusból a másikba való átalakításának képessége miatt az transformer osztály számos egyidejű hálózat kulcsösszetevője lesz. Emellett részletesebb párhuzamos funkciókat is hozzáadhat egy transformer objektum munkafüggvényéhez.
példa
Az alábbi példa bemutatja, hogyan lehet az transformer osztály alapvető struktúrájával dolgozni. Ez a példa egy transformer olyan objektumot hoz létre, amely az egyes bemeneti int értékeket 0,33-tal szorozza, hogy kimenetként értéket állíthasson elő double . A példa ezután megkapja az átalakított értékeket ugyanabból transformer az objektumból, és kinyomtatja őket a konzolon.
// transformer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create an transformer object that receives int data and
// sends double data.
transformer<int, double> third([](int n) {
// Return one-third of the input value.
return n * 0.33;
});
// Send a few items to the transformer object.
send(third, 33);
send(third, 44);
send(third, 55);
// Read the processed items from the transformer object and print
// them to the console.
wcout << receive(third) << endl;
wcout << receive(third) << endl;
wcout << receive(third) << endl;
}
Ez a példa a következő kimenetet hozza létre:
10.8914.5218.15
Az transformer osztály használatát bemutató teljes példáért lásd Hogyan: Használja a transformer-t egy adatfolyamatban.
[Felső]
választási osztály
Az egyidejűség::choice osztály kiválasztja az első elérhető üzenetet egy forráskészletből. Az choice osztály adatfolyam-mechanizmus helyett vezérlőfolyamat-mechanizmust jelöl (az Aszinkron ügynökök kódtára az adatfolyam és a vezérlőfolyamat közötti különbségeket ismerteti).
Egy választási objektumból való olvasás hasonlít a Windows API-függvény WaitForMultipleObjects meghívására, amikor a bWaitAll paraméter értéke FALSE. Az choice osztály azonban nem külső szinkronizálási objektumhoz, hanem magához az eseményhez köti az adatokat.
Általában az choice osztályt a concurrency::receive függvénnyel együtt használja, hogy irányítsa a vezérlési folyamatot az alkalmazásában. Akkor használja az choice osztályt, ha különböző típusú üzenetpufferek közül kell választania. Akkor használja az single_assignment osztályt, ha azonos típusú üzenetpufferek közül kell választania.
A források objektumhoz való choice csatolásának sorrendje azért fontos, mert meghatározhatja, hogy melyik üzenet van kiválasztva. Vegyük például azt az esetet, amikor több üzenetpuffert csatol, amelyek már tartalmaznak üzenetet egy choice objektumhoz. Az choice objektum az első forrásból választja ki az üzenetet, amelyhez kapcsolódik. Miután összekapcsolta az összes forrást, az choice objektum megőrzi azt a sorrendet, amelyben az egyes források üzenetet kapnak.
példa
Az alábbi példa bemutatja, hogyan lehet az choice osztály alapvető struktúrájával dolgozni. Ez a példa az egyidejűség::make_choice függvénnyel hoz létre egy choice objektumot, amely három üzenetblokk közül választ. A példa ezután kiszámítja a különböző Fibonacci-számokat, és az egyes eredményeket egy másik üzenetblokkban tárolja. A példa ezután a konzolra nyomtat egy üzenetet, amely az elsőként befejezett műveleten alapul.
// choice-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
if (n < 2)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
int wmain()
{
// Although the following thee message blocks are written to one time only,
// this example illustrates the fact that the choice class works with
// different message block types.
// Holds the 35th Fibonacci number.
single_assignment<int> fib35;
// Holds the 37th Fibonacci number.
overwrite_buffer<int> fib37;
// Holds half of the 42nd Fibonacci number.
unbounded_buffer<double> half_of_fib42;
// Create a choice object that selects the first single_assignment
// object that receives a value.
auto select_one = make_choice(&fib35, &fib37, &half_of_fib42);
// Execute a few lengthy operations in parallel. Each operation sends its
// result to one of the single_assignment objects.
parallel_invoke(
[&fib35] { send(fib35, fibonacci(35)); },
[&fib37] { send(fib37, fibonacci(37)); },
[&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
);
// Print a message that is based on the operation that finished first.
switch (receive(select_one))
{
case 0:
wcout << L"fib35 received its value first. Result = "
<< receive(fib35) << endl;
break;
case 1:
wcout << L"fib37 received its value first. Result = "
<< receive(fib37) << endl;
break;
case 2:
wcout << L"half_of_fib42 received its value first. Result = "
<< receive(half_of_fib42) << endl;
break;
default:
wcout << L"Unexpected." << endl;
break;
}
}
Ez a példa a következő mintakimenetet hozza létre:
fib35 received its value first. Result = 9227465
Mivel a 35. Fibonacci-számot kiszámító tevékenység nem garantáltan előbb fejeződik be, a példa kimenete eltérő lehet.
Ez a példa a concurrency::parallel_invoke algoritmust használja a Fibonacci-számok párhuzamos kiszámítására. További információ: parallel_invokePárhuzamos algoritmusok.
Az osztály használatát choice bemutató teljes példáért lásd : Útmutató: Kiválasztás befejezett tevékenységek között.
[Felső]
Join és Multitype_join osztályok
Az egyidejűség::illesztés és egyidejűség::multitype_join osztályokkal megvárhatja, amíg egy forráscsoport minden tagja megkapja az üzenetet. Az join osztály olyan forrásobjektumokon működik, amelyek gyakran használt üzenettípussal rendelkeznek. Az multitype_join osztály olyan forrásobjektumokon működik, amelyek különböző üzenettípusokkal rendelkezhetnek.
Olvasás join vagy multitype_join objektumból hasonlít a Windows API-függvény WaitForMultipleObjects meghívására, amely esetében a bWaitAll paraméter TRUE értékre van beállítva. Az objektumokhoz hasonlóan choice azonban az objektumok is olyan eseménymechanizmust használnak, joinmultitype_join amely az adatokat magához az eseményhez köti, nem pedig egy külső szinkronizálási objektumhoz.
Az join objektumból való olvasás std::vector objektumot hoz létre. Az multitype_join objektumból való olvasás std::tuple objektumot hoz létre. Az elemek ugyanabban a sorrendben jelennek meg ezekben az objektumokban, mint a megfelelő forráspufferek, amelyek az join vagy multitype_join objektumhoz vannak csatolva. Mivel a forráspufferek egy join vagy multitype_join objektumhoz való csatolásának sorrendje az vector vagy tuple objektum elemeinek sorrendjéhez van társítva, javasoljuk, hogy ne szüntesse meg egy meglévő forráspuffer társítását az összekapcsolásból. Ez meghatározatlan viselkedést eredményezhet.
Mohó csatlakozások szemben a nem mohó csatlakozásokkal
Az join és multitype_join osztályok támogatják a kapzsi és a nem kapzsi csatlakozások fogalmát. A mohó csatlakozások minden forrásból fogadnak üzenetet, amint az üzenetek elérhetővé válnak, amíg az összes üzenet el nem érhető. A nem mohó csatlakozás két fázisban fogadja az üzeneteket. Először is, a nem mohó csatlakozás megvárja, amíg az egyes forrásoktól üzenetet kap. Másodszor, miután az összes forrásüzenet elérhetővé válik, egy nem mohó csatlakozási kísérlet megpróbálja lefoglalni az egyes üzeneteket. Ha az egyes üzeneteket lefoglalhatja, az összes üzenetet felhasználja, és propagálja a célnak. Ellenkező esetben kiadja vagy megszakítja az üzenetfoglalásokat, és ismét megvárja, amíg minden forrás megkapja az üzenetet.
A nagylelkű illesztések jobban teljesítenek, mint az óvatos illesztések, mert azonnal fogadják az üzeneteket. Ritkán azonban a mohó illesztések holtpontokhoz vezethetnek. Használjon nem gazdag illesztést, amikor több olyan illesztés is van, amely egy vagy több közös forrásobjektumot tartalmaz.
példa
Az alábbi példa bemutatja, hogyan lehet az join osztály alapvető struktúrájával dolgozni. Ez a példa az egyidejűség::make_join függvénnyel hoz létre egy join objektumot, amely három single_assignment objektumból fogad. Ez a példa különböző Fibonacci-számokat számít ki, az egyes eredményeket egy másik single_assignment objektumban tárolja, majd a konzolra nyomtatja az join objektum által tárolt összes eredményt. Ez a példa hasonló az osztály példájához, azzal a choice kivételrel, hogy az osztály megvárja, amíg az join összes forrásüzenetblokk fogad egy üzenetet.
// join-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
if (n < 2)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
int wmain()
{
// Holds the 35th Fibonacci number.
single_assignment<int> fib35;
// Holds the 37th Fibonacci number.
single_assignment<int> fib37;
// Holds half of the 42nd Fibonacci number.
single_assignment<double> half_of_fib42;
// Create a join object that selects the values from each of the
// single_assignment objects.
auto join_all = make_join(&fib35, &fib37, &half_of_fib42);
// Execute a few lengthy operations in parallel. Each operation sends its
// result to one of the single_assignment objects.
parallel_invoke(
[&fib35] { send(fib35, fibonacci(35)); },
[&fib37] { send(fib37, fibonacci(37)); },
[&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
);
auto result = receive(join_all);
wcout << L"fib35 = " << get<0>(result) << endl;
wcout << L"fib37 = " << get<1>(result) << endl;
wcout << L"half_of_fib42 = " << get<2>(result) << endl;
}
Ez a példa a következő kimenetet hozza létre:
fib35 = 9227465fib37 = 24157817half_of_fib42 = 1.33957e+008
Ez a példa a concurrency::parallel_invoke algoritmust használja a Fibonacci-számok párhuzamos kiszámítására. További információ: parallel_invokePárhuzamos algoritmusok.
Az osztály használatát join szemléltető példákért lásd : Útmutató: Kiválasztás befejezett feladatok között és útmutató: Csatlakozás használata a holtpont megelőzéséhez.
[Felső]
időzítőosztály
A párhuzamosság::timer osztály üzenetforrásként működik. Egy timer objektum egy adott időtartam leteltét követően üzenetet küld a célnak. Az timer osztály akkor hasznos, ha késleltetnie kell az üzenet küldését, vagy ha rendszeres időközönként szeretne üzenetet küldeni.
Az timer osztály csak egy célnak küldi el az üzenetét. Ha a _PTarget paramétert a konstruktorban NULL értékre állítja, később megadhatja a célt a concurrency::ISource::link_target metódus meghívásával.
Egy timer objektum lehet ismétlődő vagy nem ismétlődő. Ismétlődő időzítő létrehozásához true paraméterként adja meg _Repeating a konstruktor meghívásakor. Ellenkező esetben adja meg a false értékét a _Repeating paraméterhez, hogy ne ismétlődő időzítőt hozzon létre. Ha az időzítő ismétlődik, minden intervallum után ugyanazt az üzenetet küldi a célnak.
Az Ügynökök tára nem elindított állapotban hoz létre timer objektumokat. Időzítőobjektum indításához hívja meg a concurrency::timer::start metódust. Egy timer objektum leállításához pusztítsa el az objektumot, vagy hívja meg a concurrency::timer::stop metódust. Ismétlődő időzítő szüneteltetéséhez hívja meg a konkurencia::timer::pause metódust.
példa
Az alábbi példa bemutatja, hogyan lehet az timer osztály alapvető struktúrájával dolgozni. A példa egy hosszadalmas művelet előrehaladásának jelentésére használ timer és call objektumokat.
// timer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
if (n < 2)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
int wmain()
{
// Create a call object that prints characters that it receives
// to the console.
call<wchar_t> print_character([](wchar_t c) {
wcout << c;
});
// Create a timer object that sends the period (.) character to
// the call object every 100 milliseconds.
timer<wchar_t> progress_timer(100u, L'.', &print_character, true);
// Start the timer.
wcout << L"Computing fib(42)";
progress_timer.start();
// Compute the 42nd Fibonacci number.
int fib42 = fibonacci(42);
// Stop the timer and print the result.
progress_timer.stop();
wcout << endl << L"result is " << fib42 << endl;
}
Ez a példa a következő mintakimenetet hozza létre:
Computing fib(42)..................................................result is 267914296
Az osztály használatát bemutató teljes példáért tekintse meg a timerHogyan: Üzenet küldése rendszeres időközönként című témakört.
[Felső]
Üzenetszűrés
Üzenetblokk-objektum létrehozásakor megadhat egy szűrőfüggvényt , amely meghatározza, hogy az üzenetblokk elfogad-e vagy elutasít egy üzenetet. A szűrőfüggvények segítségével garantálható, hogy az üzenetblokkok csak bizonyos értékeket kapnak.
Az alábbi példa bemutatja, hogyan hozhat létre olyan unbounded_buffer objektumot, amely szűrőfüggvény használatával csak páros számokat fogad el. Az unbounded_buffer objektum elutasítja a páratlan számokat, ezért nem propagálja a páratlan számokat a célblokkokra.
// filter-function.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create an unbounded_buffer object that uses a filter
// function to accept only even numbers.
unbounded_buffer<int> accept_evens(
[](int n) {
return (n%2) == 0;
});
// Send a few values to the unbounded_buffer object.
unsigned int accept_count = 0;
for (int i = 0; i < 10; ++i)
{
// The asend function returns true only if the target
// accepts the message. This enables us to determine
// how many elements are stored in the unbounded_buffer
// object.
if (asend(accept_evens, i))
{
++accept_count;
}
}
// Print to the console each value that is stored in the
// unbounded_buffer object. The unbounded_buffer object should
// contain only even numbers.
while (accept_count > 0)
{
wcout << receive(accept_evens) << L' ';
--accept_count;
}
}
Ez a példa a következő kimenetet hozza létre:
0 2 4 6 8
A szűrőfüggvény lehet lambda függvény, függvénymutató vagy függvényobjektum. Minden szűrőfüggvény az alábbi formák egyikét veszi fel.
bool (T)
bool (T const &)
Az adatok szükségtelen másolásának kiküszöböléséhez használja a második űrlapot, ha egy érték által propagált összesítési típussal rendelkezik.
Az üzenetszűrés támogatja az adatfolyam-programozási modellt, amelyben az összetevők számításokat végeznek az adatok fogadásakor. A szűrőfüggvényeket használó példák az üzenettovábbító hálózaton belüli adatáramlás szabályozására : Útmutató: Üzenetblokkszűrő használata, útmutató: Adatfolyam-ügynök létrehozása és útmutató: Image-Processing hálózat létrehozása.
[Felső]
Az üzenet helyfoglalása
Az üzenetfoglalás lehetővé teszi, hogy az üzenetblokkok lefoglaljanak egy üzenetet későbbi használatra. Az üzenetfoglalás általában nem közvetlenül használatos. Az üzenetfoglalás megértése azonban segíthet jobban megérteni néhány előre definiált üzenetblokk-típus viselkedését.
Fontolja meg a nem mohó és kapzsi csatlakozásokat. Mindkettő üzenetfoglalást használ az üzenetek későbbi használatra való lefoglalásához. A korábban ismertetett, nem mohó illesztés két fázisban fogadja az üzeneteket. Az első fázisban egy nem mohó join objektum megvárja, amíg az egyes források üzenetet kapnak. A nem mohó csatlakozás ezután megpróbál minden egyes üzenetet lefoglalni. Ha az egyes üzeneteket lefoglalhatja, az összes üzenetet felhasználja, és propagálja a célnak. Ellenkező esetben kiadja vagy megszakítja az üzenetfoglalásokat, és ismét megvárja, amíg minden forrás megkapja az üzenetet.
A mohó csatlakozás, amely több forrásból is beolvassa a bemeneti üzeneteket, az üzenetfoglalással további üzeneteket olvas be, miközben vár minden egyes forrásból érkező üzenet fogadására. Vegyük például azt a kapzsi összekapcsolódást, amely üzeneteket fogad az üzenetblokkokból A és B. Ha a mohó illesztés két üzenetet kap B-től, de még nem kapott üzenetet A, a mohó illesztés menti a második üzenet B egyedi üzenetazonosítóját. Miután a greedy join kap egy üzenetet A, és továbbítja ezeket az üzeneteket, a mentett üzenetazonosítót használja annak megállapítására, hogy a második üzenet B továbbra is elérhető-e.
Az üzenetfoglalást saját egyéni üzenetblokk-típusok implementálásakor használhatja. Az egyéni üzenetblokk-típus létrehozásáról szóló példa: Útmutató: Egyéni üzenetblokk létrehozása.
[Felső]