Asynchronní bloky zpráv
Knihovna agentů poskytuje několik typů bloků zpráv, které umožňují šíření zpráv mezi komponentami aplikace bezpečným způsobem. Tyto typy bloků zpráv se často používají s různými rutinami předávání zpráv, například concurrency::send, concurrency::asend, concurrency::receive a concurrency::try_receive. Další informace o předávání zpráv rutin, které jsou definovány knihovnou agentů, naleznete v tématu Funkce předávání zpráv.
Oddíly
Toto téma obsahuje následující části:
Zdroje a cíle
Zdroje a cíle jsou dvěma důležitými účastníky předávání zpráv. Zdroj odkazuje na koncový bod komunikace, který odesílá zprávy. Cíl odkazuje na koncový bod komunikace, který přijímá zprávy. Zdroj si můžete představit jako koncový bod, ze kterého čtete, a cíl jako koncový bod, do kterého zapisujete. Aplikace propojují zdroje a cíle dohromady s formovacími sítěmi pro zasílání zpráv.
Knihovna Agents používá dvě abstraktní třídy k reprezentaci zdrojů a cílů: concurrency::ISource a concurrency::ITarget. Typy bloku zpráv, které fungují jako zdroje, odvozeny od ISource
; typy bloku zpráv, které fungují jako cíle odvozeny od ITarget
. Typy bloků zpráv, které fungují jako zdroje a cíle, jsou odvozeny z obou ISource
a ITarget
.
[Nahoře]
Šíření zpráv
Šíření zpráv je odeslání zprávy z jedné komponenty do druhé. Když se zobrazí blok zprávy, může zprávu přijmout, odmítnout nebo odložit. Každý blok zpráv ukládá a přenáší zprávy různými způsoby. Třída například unbounded_buffer
ukládá neomezený počet zpráv, overwrite_buffer
třída ukládá vždy jednu zprávu a třída transformátoru ukládá změněnou verzi každé zprávy. Tyto typy bloků zpráv jsou podrobněji popsány dále v tomto dokumentu.
Když blok zprávy přijme zprávu, může volitelně provádět práci a pokud je blok zpráv zdrojem, předejte výslednou zprávu jinému členu sítě. Blok zpráv může pomocí funkce filtru odmítnout zprávy, které nechce přijímat. Filtry jsou podrobněji popsány dále v tomto tématu v části Filtrování zpráv. Blok zprávy, který zprávu odloží, si může tuto zprávu rezervovat a později ji využívat. Rezervace zpráv je podrobněji popsána dále v tomto tématu v části Rezervace zpráv.
Knihovna agentů umožňuje blokům zpráv asynchronně nebo synchronně předávat zprávy. Když předáte zprávu do bloku zprávy synchronně, například pomocí send
funkce, modul runtime blokuje aktuální kontext, dokud cílový blok zprávu přijme nebo odmítne. Když předáte zprávu bloku zprávy asynchronně, například pomocí asend
funkce, modul runtime nabídne zprávu cíli a pokud cíl zprávu přijme, modul runtime naplánuje asynchronní úlohu, která zprávu rozšíří do příjemce. Modul runtime používá zjednodušené úlohy k šíření zpráv kooperativním způsobem. Další informace o jednoduchých úkolech naleznete v tématu Plánovač úloh.
Aplikace propojují zdroje a cíle dohromady s formovacími sítěmi pro zasílání zpráv. Obvykle propojíte síť a zavoláte send
nebo asend
předáte data do sítě. Pokud chcete připojit blok zdrojové zprávy k cíli, zavolejte metodu concurrency::ISource::link_target . Chcete-li odpojit zdrojový blok od cíle, zavolejte metodu concurrency::ISource::unlink_target . Pokud chcete odpojit zdrojový blok od všech cílů, zavolejte metodu concurrency::ISource::unlink_targets . Když některý z předdefinovaných typů bloků zpráv opustí obor nebo je zničen, automaticky se odpojí od všech cílových bloků. Některé typy bloků zpráv omezují maximální počet cílů, do nichž mohou zapisovat. Následující část popisuje omezení, která platí pro předdefinované typy bloků zpráv.
[Nahoře]
Přehled typů bloků zpráv
Následující tabulka stručně popisuje roli důležitých typů bloků zpráv.
unbounded_buffer
Ukládá frontu zpráv.
overwrite_buffer
Ukládá jednu zprávu, do které lze zapisovat a číst vícekrát.
single_assignment
Ukládá jednu zprávu, která se dá zapsat najednou a číst z vícekrát.
zavolat
Provádí práci, když obdrží zprávu.
transformátor
Provádí práci, když přijímá data a odesílá výsledek této práce do jiného cílového bloku. Třída transformer
může pracovat s různými vstupními a výstupními typy.
volba
Vybere první dostupnou zprávu ze sady zdrojů.
join and multitype join
Počkejte, až budou všechny zprávy přijaty ze sady zdrojů, a pak je zkombinujte do jedné zprávy pro jiný blok zpráv.
časovač
Odešle zprávu do cílového bloku v pravidelných intervalech.
Tyto typy bloků zpráv mají různé charakteristiky, díky kterým jsou užitečné pro různé situace. Toto jsou některé vlastnosti:
Typ šíření: Určuje, zda blok zprávy funguje jako zdroj dat, příjemce dat nebo obojí.
Řazení zpráv: Určuje, jestli blok zprávy udržuje původní pořadí, ve kterém se zprávy odesílají nebo přijímají. Každý předdefinovaný typ bloku zprávy udržuje původní pořadí, ve kterém odesílá nebo přijímá zprávy.
Počet zdrojů: Maximální počet zdrojů, ze kterého může blok zpráv číst.
Cílový počet: Maximální počet cílů, na které může blok zpráv zapisovat.
Následující tabulka ukazuje, jak tyto charakteristiky souvisejí s různými typy bloků zpráv.
Typ bloku zprávy | Typ šíření (zdroj, cíl nebo obojí) | Řazení zpráv (seřazené nebo neuspořádané) | Počet zdrojů | Cílový počet |
---|---|---|---|---|
unbounded_buffer |
Oba | Objednáno | Neomezený | Neomezený |
overwrite_buffer |
Oba | Objednáno | Neomezený | Neomezený |
single_assignment |
Oba | Objednáno | Neomezený | Neomezený |
call |
Cíl | Objednáno | Neomezený | Neuvedeno |
transformer |
Oba | Objednáno | Neomezený | 0 |
choice |
Oba | Objednáno | 10 | 0 |
join |
Oba | Objednáno | Neomezený | 0 |
multitype_join |
Oba | Objednáno | 10 | 0 |
timer |
Zdroj | Neuvedeno | Neuvedeno | 0 |
Následující části popisují typy bloků zpráv podrobněji.
[Nahoře]
Třída unbounded_buffer
Concurrency::unbounded_buffer třída představuje strukturu asynchronního zasílání zpráv pro obecné účely. Tato třída ukládá první do fronty fiFO (first out) zpráv, které lze zapisovat do více zdrojů nebo číst z více cílů. Když cíl přijme zprávu z objektu unbounded_buffer
, tato zpráva se odebere z fronty zpráv. I když unbounded_buffer
objekt může mít více cílů, obdrží každá zpráva pouze jeden cíl. Třída unbounded_buffer
je užitečná, když chcete předat více zpráv jiné komponentě a tato komponenta musí přijímat každou zprávu.
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s unbounded_buffer
třídou. Tento příklad odešle do objektu unbounded_buffer
tři hodnoty a pak tyto hodnoty načte zpět ze stejného objektu.
// 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;
}
Tento příklad vytvoří následující výstup:
334455
Úplný příklad, který ukazuje, jak používat unbounded_buffer
třídu, viz Postupy: Implementace různých vzorů producent-příjemce.
[Nahoře]
Třída overwrite_buffer
Souběžnost::overwrite_buffer třída se podobá unbounded_buffer
třídě s tím rozdílem, že overwrite_buffer
objekt ukládá jen jednu zprávu. Kromě toho, když cíl obdrží zprávu z objektu overwrite_buffer
, tato zpráva není odebrána z vyrovnávací paměti. Proto více cílů obdrží kopii zprávy.
Třída overwrite_buffer
je užitečná, když chcete předat více zpráv jiné komponentě, ale tato komponenta potřebuje pouze nejnovější hodnotu. Tato třída je užitečná také v případě, že chcete zprávu vysílat více komponentám.
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s overwrite_buffer
třídou. Tento příklad odešle do objektu overwrite _buffer
tři hodnoty a pak přečte aktuální hodnotu ze stejného objektu třikrát. Tento příklad je podobný příkladu unbounded_buffer
pro třídu. overwrite_buffer
Třída ale ukládá jenom jednu zprávu. Modul runtime navíc po přečtení neodebere zprávu z objektu overwrite_buffer
.
// 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;
}
Tento příklad vytvoří následující výstup:
555555
Úplný příklad, který ukazuje, jak používat overwrite_buffer
třídu, viz Postupy: Implementace různých vzorů producent-příjemce.
[Nahoře]
Třída single_assignment
Souběžnost::single_assignment třída se podobá overwrite_buffer
třídě, s tím rozdílemsingle_assignment
, že objekt lze zapsat pouze jednou. Podobně jako třída overwrite_buffer
, když cíl obdrží zprávu z objektu, tato zpráva se z objektu single_assignment
neodebere. Proto více cílů obdrží kopii zprávy. Třída single_assignment
je užitečná, když chcete vysílat jednu zprávu do více komponent.
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s single_assignment
třídou. Tento příklad odešle do objektu single_assignment
tři hodnoty a pak přečte aktuální hodnotu ze stejného objektu třikrát. Tento příklad je podobný příkladu overwrite_buffer
pro třídu. I když obě třídy overwrite_buffer
single_assignment
ukládají jednu zprávu, single_assignment
třída může být zapsána pouze na jeden čas.
// 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;
}
Tento příklad vytvoří následující výstup:
333333
Úplný příklad, který ukazuje, jak používat single_assignment
třídu, naleznete v části Návod: Implementace futures.
[Nahoře]
Třída call
Třída concurrency::call funguje jako příjemce zprávy, který při příjmu dat provádí pracovní funkci. Tato pracovní funkce může být výraz lambda, objekt funkce nebo ukazatel funkce. Objekt call
se chová jinak než běžné volání funkce, protože funguje paralelně s jinými komponentami, které do něj odesílají zprávy. call
Pokud objekt provádí práci, když obdrží zprávu, přidá tuto zprávu do fronty. Každý call
objekt zpracovává zprávy zařazené do fronty v pořadí, v jakém jsou přijímány.
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s call
třídou. Tento příklad vytvoří objekt, který vytiskne call
každou hodnotu, kterou obdrží do konzoly. Příklad pak odešle do objektu call
tři hodnoty. Vzhledem k tomu, že call
objekt zpracovává zprávy v samostatném vlákně, tento příklad také používá proměnnou čítače a objekt události k zajištění, že call
objekt zpracuje všechny zprávy před vrácením wmain
funkce.
// 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();
}
Tento příklad vytvoří následující výstup:
334455
Úplný příklad, který ukazuje, jak používat call
třídu, viz Postupy: Poskytování pracovních funkcí pro třídy volání a transformátoru.
[Nahoře]
Třída transformer
Třída concurrency::transformer funguje jako příjemce zprávy i jako odesílatel zprávy. Třída transformer
se podobá call
třídě, protože provádí uživatelsky definovanou pracovní funkci při příjmu dat. transformer
Třída však také odešle výsledek pracovní funkce do objektů přijímače. call
Podobně jako objekt funguje objekt transformer
paralelně s ostatními komponentami, které do něj odesílají zprávy. transformer
Pokud objekt provádí práci, když obdrží zprávu, přidá tuto zprávu do fronty. Každý transformer
objekt zpracovává zprávy zařazené do fronty v pořadí, v jakém jsou přijímány.
Třída transformer
odešle zprávu do jednoho cíle. Pokud nastavíte parametr v konstruktoru _PTarget
na NULL
, můžete později určit cíl voláním concurrency::link_target metoda.
Na rozdíl od všech ostatních typů bloků asynchronních zpráv, které poskytuje knihovna agentů, transformer
může třída pracovat s různými vstupními a výstupními typy. Díky této schopnosti transformovat data z jednoho typu do druhého tvoří třída klíčovou komponentu transformer
v mnoha souběžných sítích. Kromě toho můžete do pracovní funkce objektu transformer
přidat jemně odstupňované paralelní funkce.
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s transformer
třídou. Tento příklad vytvoří transformer
objekt, který násobí každou vstupní int
hodnotu o 0,33, aby se vytvořila double
hodnota jako výstup. Příklad pak obdrží transformované hodnoty ze stejného transformer
objektu a vytiskne je do konzoly.
// 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;
}
Tento příklad vytvoří následující výstup:
10.8914.5218.15
Úplný příklad, který ukazuje, jak používat transformer
třídu, viz Postupy: Použití transformátoru v datovém kanálu.
[Nahoře]
Třída choice
Třída concurrency::choice vybere první dostupnou zprávu ze sady zdrojů. Třída choice
představuje mechanismus toku řízení místo mechanismu toku dat (téma Asynchronní agenti Knihovna popisuje rozdíly mezi tokem dat a tokem řízení).
Čtení z objektu výběru se podobá volání funkce WaitForMultipleObjects
rozhraní API systému Windows, pokud má bWaitAll
parametr nastavený na FALSE
. choice
Třída však vytvoří vazbu dat k samotné události místo k externímu objektu synchronizace.
Obvykle používáte choice
třídu společně s funkcí concurrency::receive k řízení toku ve vaší aplikaci. choice
Třídu použijte, když potřebujete vybrat mezi vyrovnávacími paměťmi zpráv, které mají různé typy. single_assignment
Třídu použijte, když potřebujete vybrat mezi vyrovnávacími paměťmi zpráv, které mají stejný typ.
Pořadí propojení zdrojů s objektem choice
je důležité, protože může určit, která zpráva je vybrána. Představte si například případ, kdy propočítáte více vyrovnávacích pamětí zpráv, které již obsahují zprávu s objektem choice
. Objekt choice
vybere zprávu z prvního zdroje, ke kterému je propojen. Po propojení všech zdrojů zachová objekt pořadí, choice
ve kterém každý zdroj obdrží zprávu.
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s choice
třídou. Tento příklad používá funkci concurrency::make_choice k vytvoření choice
objektu, který vybere ze tří bloků zpráv. Příklad pak vypočítá různé fibonacciho čísla a uloží každý výsledek do jiného bloku zpráv. V příkladu se pak v konzole zobrazí zpráva založená na operaci, která byla dokončena jako první.
// 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;
}
}
Tento příklad vytvoří následující ukázkový výstup:
fib35 received its value first. Result = 9227465
Vzhledem k tomu, že úloha, která vypočítá 35. fibonacciho číslo, není zaručeno, že se dokončí jako první, může se výstup tohoto příkladu lišit.
Tento příklad používá algoritmus concurrency::p arallel_invoke k paralelnímu výpočtu fibonacciho čísel. Další informace o parallel_invoke
, naleznete v tématu Paralelní algoritmy.
Úplný příklad, který ukazuje, jak používat choice
třídu, najdete v tématu Postupy: Výběr mezi dokončené úkoly.
[Nahoře]
join and multitype_join classes
Třídy concurrency::join a concurrency::multitype_join umožňují čekat na přijetí zprávy každému členu sady zdrojů. Třída join
funguje na zdrojových objektech, které mají společný typ zprávy. Třída multitype_join
funguje na zdrojových objektech, které mohou mít různé typy zpráv.
Čtení z objektu join
nebo multitype_join
objektu se podobá volání funkce WaitForMultipleObjects
rozhraní API systému Windows, pokud má bWaitAll
parametr nastavený na TRUE
. Stejně jako choice
objekt a multitype_join
objekty používají mechanismus událostí, join
který vytvoří vazbu dat k samotné události místo k objektu externí synchronizace.
Čtení z objektu join
vytvoří objekt std::vector . Čtení z objektu multitype_join
vytvoří objekt std::tuple . Prvky se v těchto objektech zobrazují ve stejném pořadí jako odpovídající zdrojové vyrovnávací paměti jsou propojeny s objektem nebo multitype_join
objektemjoin
. Vzhledem k tomu, že pořadí propojení zdrojových vyrovnávacích pamětí k objektu join
nebo objektu multitype_join
je přidruženo k pořadí prvků ve výsledném vector
objektu nebo tuple
objektu, doporučujeme zrušit propojení existující zdrojové vyrovnávací paměti z spojení. Výsledkem tohoto chování může být nespecifikované chování.
Porovnání spojení typu Greedy a Non-Greedy
multitype_join
Třídy join
podporují koncept greedy a non-greedy spojení. Greedy join přijímá zprávu z každého ze svých zdrojů, jakmile budou zprávy k dispozici, dokud nebudou k dispozici všechny zprávy. Spojení bez greedy přijímá zprávy ve dvou fázích. Za prvé, ne greedy spojení čeká, dokud se nenabízí zpráva z každého z jeho zdrojů. Za druhé, jakmile jsou všechny zdrojové zprávy k dispozici, pokusí se připojení bez greedy rezervovat každou z těchto zpráv. Pokud si může každá zpráva rezervovat, spotřebovává všechny zprávy a rozšíří je do cíle. V opačném případě uvolní nebo zruší rezervace zpráv a znovu počká, až každý zdroj obdrží zprávu.
Spojení Greedy fungují lépe než spojení bez greedy, protože přijímají zprávy okamžitě. Ve výjimečných případech však můžou spojení greedy vést k zablokování. Připojení bez greedy použijte, pokud máte více spojení, která obsahují jeden nebo více sdílených zdrojových objektů.
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s join
třídou. Tento příklad používá funkci concurrency::make_join k vytvoření join
objektu, který přijímá ze tří single_assignment
objektů. Tento příklad vypočítá různé fibonacciho čísla, uloží každý výsledek do jiného single_assignment
objektu a pak se vytiskne do konzoly každého výsledku join
, který objekt obsahuje. Tento příklad je podobný příkladu choice
pro třídu s tím rozdílem, že join
třída čeká na přijetí zprávy všemi zdrojovými bloky zpráv.
// 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;
}
Tento příklad vytvoří následující výstup:
fib35 = 9227465fib37 = 24157817half_of_fib42 = 1.33957e+008
Tento příklad používá algoritmus concurrency::p arallel_invoke k paralelnímu výpočtu fibonacciho čísel. Další informace o parallel_invoke
, naleznete v tématu Paralelní algoritmy.
Kompletní příklady, které ukazují, jak používat join
třídu, najdete v tématu Postupy: Výběr mezi dokončené úlohy a návod: Použití spojení k zabránění vzájemnému zablokování.
[Nahoře]
Třída timer
Třída concurrency::timer funguje jako zdroj zprávy. Objekt timer
odešle zprávu cíli po uplynutí zadaného časového období. Třída timer
je užitečná, když je nutné zpozdit odeslání zprávy nebo chcete zprávu odeslat v pravidelných intervalech.
Třída timer
odešle zprávu pouze do jednoho cíle. Pokud nastavíte parametr v konstruktoru _PTarget
na NULL
, můžete později určit cíl voláním concurrency::ISource::link_target metoda.
Objekt timer
se může opakovat nebo neopakovat. Chcete-li vytvořit opakující se časovač, předejte true
_Repeating
parametr při volání konstruktoru. Jinak předejte false
_Repeating
parametru, aby se vytvořil neopakující časovač. Pokud se časovač opakuje, odešle stejnou zprávu do cíle po každém intervalu.
Knihovna agentů vytváří timer
objekty v nezahajované stavu. Pokud chcete spustit objekt časovače, zavolejte metodu concurrency::timer::start . Chcete-li zastavit timer
objekt, zničit objekt nebo volat concurrency::timer::stop metoda. Pokud chcete pozastavit opakující se časovač, zavolejte metodu concurrency::timer::p ause .
Příklad
Následující příklad ukazuje základní strukturu, jak pracovat s timer
třídou. Příklad používá timer
a call
objekty k hlášení průběhu zdlouhavé operace.
// 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;
}
Tento příklad vytvoří následující ukázkový výstup:
Computing fib(42)..................................................result is 267914296
Úplný příklad, který ukazuje, jak používat timer
třídu, naleznete v tématu Postupy: Odeslání zprávy v pravidelném intervalu.
[Nahoře]
Filtrování zpráv
Při vytváření objektu bloku zprávy můžete zadat funkci filtru, která určuje, zda blok zprávy přijme nebo odmítne zprávu. Funkce filtru je užitečný způsob, jak zaručit, že blok zpráv přijímá pouze určité hodnoty.
Následující příklad ukazuje, jak vytvořit unbounded_buffer
objekt, který používá funkci filtru k přijetí pouze sudých čísel. Objekt unbounded_buffer
odmítne lichá čísla, a proto nerozšířuje lichá čísla do cílových bloků.
// 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;
}
}
Tento příklad vytvoří následující výstup:
0 2 4 6 8
Funkce filtru může být funkce lambda, ukazatel funkce nebo objekt funkce. Každá funkce filtru má jednu z následujících forem.
bool (T)
bool (T const &)
Pokud chcete eliminovat zbytečné kopírování dat, použijte druhý formulář, pokud máte agregovaný typ, který se šíří podle hodnoty.
Filtrování zpráv podporuje programovací model toku dat, ve kterém komponenty provádějí výpočty při příjmu dat. Příklady, které používají funkce filtru k řízení toku dat v síti s předáváním zpráv, najdete v tématu Postupy: Použití filtru bloku zpráv, Návod: Vytvoření agenta toku dat a návod: Vytvoření sítě pro zpracování obrázků.
[Nahoře]
Rezervace zpráv
Rezervace zpráv umožňuje blokování zpráv rezervovat zprávu pro pozdější použití. Rezervace zpráv se obvykle nepoužívá přímo. Pochopení rezervace zpráv vám ale může pomoct lépe porozumět chování některých předdefinovaných typů bloků zpráv.
Zvažte nes greedy a greedy spojení. Obě tyto rezervace zprávy používají k rezervaci zpráv pro pozdější použití. Popsané dříve, bez greedy spojení přijímá zprávy ve dvou fázích. V první fázi čeká objekt, který není greedy join
, až každý zdroj obdrží zprávu. Připojení, které není greedy, se pak pokusí rezervovat všechny tyto zprávy. Pokud si může každá zpráva rezervovat, spotřebovává všechny zprávy a rozšíří je do cíle. V opačném případě uvolní nebo zruší rezervace zpráv a znovu počká, až každý zdroj obdrží zprávu.
Greedy join, který také čte vstupní zprávy z řady zdrojů, používá rezervaci zpráv ke čtení dalších zpráv, zatímco čeká na přijetí zprávy z každého zdroje. Představte si například greedy spojení, které přijímá zprávy z bloků A
zpráv a B
. Pokud spojení greedy obdrží dvě zprávy z B, ale ještě neobdržel zprávu od A
, greedy join uloží jedinečný identifikátor zprávy pro druhou zprávu z B
. Jakmile spojení greedy přijme zprávu a A
rozšíří tyto zprávy, použije uložený identifikátor zprávy k tomu, aby zjistil, jestli druhá zpráva z B
je stále dostupná.
Rezervaci zpráv můžete použít při implementaci vlastních typů bloků zpráv. Příklad vytvoření vlastního typu bloku zpráv najdete v části Návod: Vytvoření vlastního bloku zpráv.
[Nahoře]