Sdílet prostřednictvím


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.

Volání
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 Obojí Objednáno Neomezeně Neomezeně
overwrite_buffer Obojí Objednáno Neomezeně Neomezeně
single_assignment Obojí Objednáno Neomezeně Neomezeně
call Cíl Objednáno Neomezeně Neuvedeno
transformer Obojí Objednáno Neomezeně 1
choice Obojí Objednáno 10 1
join Obojí Objednáno Neomezeně 1
multitype_join Obojí Objednáno 10 1
timer Source Neuvedeno Neuvedeno 1

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_buffersingle_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]

Viz také

Knihovna asynchronních agentů