Delen via


Asynchrone berichtblokken

De agentsbibliotheek biedt verschillende typen berichtenblokken waarmee u berichten kunt doorgeven tussen toepassingsonderdelen op een thread-veilige manier. Deze typen berichtenblokken worden vaak gebruikt met de verschillende berichtdoorgifteroutines, zoals gelijktijdigheid::verzenden, gelijktijdigheid::asend, gelijktijdigheid:::ontvangst en gelijktijdigheid:::try_receive. Zie Message Passing Functions voor meer informatie over de berichtdoorgifteroutines die zijn gedefinieerd door de agentsbibliotheek.

Afdelingen

Dit onderwerp bevat de volgende secties:

Bronnen en doelen

Bronnen en doelen zijn twee belangrijke deelnemers aan het doorgeven van berichten. Een bron verwijst naar een eindpunt van communicatie waarmee berichten worden verzonden. Een doel verwijst naar een eindpunt van communicatie dat berichten ontvangt. U kunt een bron beschouwen als een eindpunt waaruit u leest en een doel als eindpunt waarnaar u schrijft. Toepassingen verbinden bronnen en doelen samen om berichtennetwerken te vormen.

De agentsbibliotheek maakt gebruik van twee abstracte klassen om bronnen en doelen weer te geven: gelijktijdigheid::ISource en gelijktijdigheid::ITarget. Berichtbloktypen die fungeren als bronnen die zijn afgeleid van ISource; berichtbloktypen die fungeren als doelen die zijn afgeleid van ITarget. Berichtbloktypen die fungeren als bronnen en doelen, die afgeleid zijn van zowel ISource als ITarget.

[Boven]

Berichtverspreiding

Berichtdoorgifte is de handeling van het verzenden van een bericht van het ene onderdeel naar het andere. Wanneer een berichtblok wordt aangeboden, kan het dat bericht accepteren, weigeren of uitstellen. Elk berichtbloktype slaat berichten op verschillende manieren op en verzendt deze. De klasse slaat bijvoorbeeld unbounded_buffer een onbeperkt aantal berichten op, de overwrite_buffer klasse slaat één bericht tegelijk op en de transformatorklasse slaat een gewijzigde versie van elk bericht op. Deze berichtbloktypen worden verderop in dit document uitgebreider beschreven.

Wanneer een berichtblok een bericht accepteert, kan het eventueel werk uitvoeren en, als het berichtblok een bron is, het resulterende bericht doorgeven aan een ander lid van het netwerk. Een berichtblok kan een filterfunctie gebruiken om berichten te weigeren die niet mogen worden ontvangen. Filters worden verderop in dit onderwerp nader beschreven, in de sectie Berichtfiltering. Een berichtblok dat een bericht uitstelt, kan dat bericht reserveren en later gebruiken. Berichtreservering wordt verderop in dit onderwerp beschreven in de sectie Berichtreservering.

Met de Agentsbibliotheek kunnen berichtblokken asynchroon of synchroon berichten doorgeven. Wanneer u een bericht synchroon doorgeeft aan een berichtblok, bijvoorbeeld door de send functie te gebruiken, blokkeert de runtime de huidige context totdat het doelblok het bericht accepteert of weigert. Wanneer u een bericht asynchroon doorgeeft aan een berichtblok, bijvoorbeeld door de asend functie te gebruiken, biedt de runtime het bericht aan het doel en als het doel het bericht accepteert, plant de runtime een asynchrone taak die het bericht doorgeeft aan de ontvanger. De runtime maakt gebruik van lichtgewicht taken om berichten op een coöperatieve manier door te geven. Zie Task Scheduler voor meer informatie over lichtgewicht taken.

Toepassingen verbinden bronnen en doelen samen om berichtennetwerken te vormen. Normaal gesproken koppelt u het netwerk en roept u send aan of geeft u asend gegevens door aan het netwerk. Als u een bronberichtblok wilt verbinden met een doel, roept u de concurrency::ISource::link_target methode aan. Als u een bronblok van een doel wilt loskoppelen, roept u de concurrency::ISource::unlink_target methode aan. Als u een bronblok van alle doelen wilt loskoppelen, roep de concurrency::ISource::unlink_targets methode aan. Wanneer een van de vooraf gedefinieerde berichtbloktypen het bereik verlaat of wordt vernietigd, wordt automatisch de verbinding met alle doelblokken verbroken. Sommige berichtbloktypen beperken het maximum aantal doelen waarnaar ze kunnen schrijven. In de volgende sectie worden de beperkingen beschreven die van toepassing zijn op de vooraf gedefinieerde typen berichtenblokken.

[Boven]

Overzicht van berichtbloktypen

In de volgende tabel wordt kort de rol van de belangrijke typen berichtenblokken beschreven.

unbounded_buffer
Slaat een wachtrij met berichten op.

overwrite_buffer
Slaat één bericht op dat meerdere keren kan worden geschreven naar en gelezen.

enkele_toewijzing
Slaat één bericht op dat één keer kan worden geschreven en meerdere keren kan worden gelezen.

bellen
Voert werk uit wanneer er een bericht wordt ontvangen.

transformator
Hiermee wordt werk uitgevoerd wanneer er gegevens worden ontvangen en het resultaat van dat werk naar een ander doelblok wordt verzonden. De transformer klasse kan reageren op verschillende invoer- en uitvoertypen.


Hiermee selecteert u het eerste beschikbare bericht uit een set bronnen.

koppeling en multitype-koppeling
Wacht tot alle berichten van een set bronnen zijn ontvangen en combineer vervolgens de berichten in het ene bericht voor een ander berichtblok.

timer
Verzendt een bericht naar een doelblok met een regelmatig interval.

Deze typen berichtenblokken hebben verschillende kenmerken die ze nuttig maken voor verschillende situaties. Dit zijn enkele van de kenmerken:

  • Doorgiftetype: Of het berichtblok fungeert als gegevensbron, een ontvanger van gegevens of beide.

  • Volgorde van berichten: of het berichtblok de oorspronkelijke volgorde behoudt waarin berichten worden verzonden of ontvangen. Elk vooraf gedefinieerd berichtbloktype behoudt de oorspronkelijke volgorde waarin berichten worden verzonden of ontvangen.

  • Aantal bronnen: het maximum aantal bronnen waaruit het berichtblok kan lezen.

  • Aantal doelen: het maximum aantal doelen waarnaar het berichtblok kan schrijven.

In de volgende tabel ziet u hoe deze kenmerken betrekking hebben op de verschillende typen berichtenblokken.

Berichtbloktype Doorgiftetype (bron, doel of beide) Berichtvolgorde (gesorteerd of ongeordend) Aantal bronnen Aantal doelen
unbounded_buffer Beide Besteld Onbegrensd Onbegrensd
overwrite_buffer Beide Besteld Onbegrensd Onbegrensd
single_assignment Beide Besteld Onbegrensd Onbegrensd
call Doel Besteld Onbegrensd Niet van toepassing
transformer Beide Besteld Onbegrensd 1
choice Beide Besteld 10 1
join Beide Besteld Onbegrensd 1
multitype_join Beide Besteld 10 1
timer Bron Niet van toepassing Niet van toepassing 1

In de volgende secties worden de typen berichtenblokken gedetailleerder beschreven.

[Boven]

unbounded_buffer-klasse

De concurrency::unbounded_buffer class vertegenwoordigt een algemeen bruikbare asynchrone berichtstructuur. In deze klasse wordt een FIFO-wachtrij (Eerst In, Eerst Uit) van berichten opgeslagen waarin meerdere bronnen kunnen schrijven of waaruit meerdere doelen kunnen lezen. Wanneer een doel een bericht van een unbounded_buffer object ontvangt, wordt dat bericht verwijderd uit de berichtenwachtrij. Hoewel een unbounded_buffer object meerdere doelen kan hebben, ontvangt slechts één doel elk bericht. De unbounded_buffer klasse is handig als u meerdere berichten wilt doorgeven aan een ander onderdeel en dat onderdeel moet elk bericht ontvangen.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de unbounded_buffer klasse. In dit voorbeeld worden drie waarden naar een unbounded_buffer object verzonden en worden deze waarden vervolgens van hetzelfde object gelezen.

// 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;
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

334455

Zie unbounded_buffer voor een volledig voorbeeld waarin wordt getoond hoe u de klasse kunt gebruiken.

[Boven]

overwrite_buffer-klasse

De gelijktijdigheid::overwrite_buffer klasse lijkt op de unbounded_buffer klasse, behalve dat een overwrite_buffer object slechts één bericht opslaat. Wanneer een doel een bericht van een overwrite_buffer object ontvangt, wordt dat bericht bovendien niet uit de buffer verwijderd. Daarom ontvangen meerdere doelen een kopie van het bericht.

De overwrite_buffer klasse is handig als u meerdere berichten wilt doorgeven aan een ander onderdeel, maar dat onderdeel heeft alleen de meest recente waarde nodig. Deze klasse is ook handig als u een bericht naar meerdere onderdelen wilt uitzenden.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de overwrite_buffer klasse. In dit voorbeeld worden drie waarden naar een overwrite _buffer object verzonden en wordt de huidige waarde van hetzelfde object drie keer gelezen. Dit voorbeeld is vergelijkbaar met het voorbeeld voor de unbounded_buffer klasse. In de overwrite_buffer klas wordt echter slechts één bericht opgeslagen. Bovendien verwijdert de runtime het bericht niet uit een overwrite_buffer object nadat het is gelezen.

// 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;
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

555555

Zie overwrite_buffer voor een volledig voorbeeld waarin wordt getoond hoe u de klasse kunt gebruiken.

[Boven]

klasse met enkele toewijzing

De concurrency::single_assignment klasse lijkt op de overwrite_buffer klasse, behalve dat een single_assignment object slechts één keer kan worden beschreven. Net als bij de overwrite_buffer klasse, wanneer een doel een bericht van een single_assignment object ontvangt, wordt dat bericht niet uit dat object verwijderd. Daarom ontvangen meerdere doelen een kopie van het bericht. De single_assignment klasse is handig als u één bericht naar meerdere onderdelen wilt uitzenden.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de single_assignment klasse. In dit voorbeeld worden drie waarden naar een single_assignment object verzonden en wordt de huidige waarde van hetzelfde object drie keer gelezen. Dit voorbeeld is vergelijkbaar met het voorbeeld voor de overwrite_buffer klasse. Hoewel zowel de overwrite_buffer als de single_assignment klassen een enkel bericht opslaan, kan de single_assignment klasse slechts één keer worden beschreven.

// 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;
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

333333

Zie single_assignment voor een volledig voorbeeld dat laat zien hoe u de klasse gebruikt.

[Boven]

klasse aanroepen

De gelijktijdigheid::oproepklasse fungeert als een berichtontvanger die een werkfunctie uitvoert wanneer er gegevens worden ontvangen. Deze werkfunctie kan een lambda-expressie, een functieobject of een functiepointer zijn. Een call object gedraagt zich anders dan een gewone functieaanroep omdat het parallel werkt met andere onderdelen die berichten naar het object verzenden. Als een call object werkt wanneer een bericht wordt ontvangen, wordt dat bericht toegevoegd aan een wachtrij. Elk call object verwerkt berichten in de wachtrij in de volgorde waarin ze worden ontvangen.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de call klasse. In dit voorbeeld wordt een call object gemaakt waarmee elke waarde wordt afgedrukt die naar de console wordt ontvangen. In het voorbeeld worden vervolgens drie waarden naar het call object verzonden. Omdat het call object berichten op een afzonderlijke thread verwerkt, gebruikt dit voorbeeld ook een tellervariabele en een gebeurtenisobject om ervoor te zorgen dat het call object alle berichten verwerkt voordat de wmain functie retourneert.

// 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();
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

334455

Voor een volledig voorbeeld waarin wordt getoond hoe u de call klasse kunt gebruiken, raadpleegt u Het volgende: Werkfuncties leveren aan de aanroep- en transformatieklassen.

[Boven]

transformatorklasse

De gelijktijdigheidsklasse::transformer fungeert als zowel een ontvanger van een bericht als als een afzender van een bericht. De transformer-klasse lijkt op de call-klasse omdat het een door de gebruiker gedefinieerde functie uitvoert wanneer er gegevens worden ontvangen. De transformer klasse verzendt echter ook het resultaat van de werkfunctie naar ontvangerobjecten. Net als een call object fungeert een transformer object parallel met andere onderdelen die berichten naar het object verzenden. Als een transformer object werkt wanneer een bericht wordt ontvangen, wordt dat bericht toegevoegd aan een wachtrij. Elk transformer object verwerkt de berichten in de wachtrij in de volgorde waarin ze worden ontvangen.

De transformer klasse verzendt het bericht naar één doel. Als u de _PTarget parameter in de constructor instelt op NULL, kunt u later het doel opgeven door de concurrency::link_target-methode aan te roepen.

In tegenstelling tot alle andere asynchrone berichtbloktypen die worden geleverd door de agentsbibliotheek, kan de transformer klasse reageren op verschillende invoer- en uitvoertypen. Deze mogelijkheid om gegevens van het ene type naar het andere te transformeren, maakt de transformer klasse een belangrijk onderdeel in veel gelijktijdige netwerken. Daarnaast kunt u meer verfijnde parallelle functionaliteit toevoegen in de werkfunctie van een transformer object.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de transformer klasse. In dit voorbeeld wordt een transformer object gemaakt dat elke invoerwaarde int met 0,33 veelvoudt om een double waarde als uitvoer te produceren. Het voorbeeld ontvangt vervolgens de getransformeerde waarden van hetzelfde transformer object en drukt deze af op de console.

// 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;
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

10.8914.5218.15

Voor een volledig voorbeeld waarin wordt getoond hoe u de transformer klasse gebruikt, raadpleegt u How to: Use transformer in a Data Pipeline.

[Boven]

keuzeklasse

De gelijktijdigheid::keuzeklasse selecteert het eerste beschikbare bericht uit een set bronnen. De choice class vertegenwoordigt een besturingsstroommechanisme in plaats van een gegevensstroommechanisme (het onderwerp Asynchrone agentsbibliotheek beschrijft de verschillen tussen besturingsstroom en gegevensstroom).

Lezen vanuit een keuzeobject lijkt op het aanroepen van de Windows API-functie WaitForMultipleObjects wanneer de bWaitAll parameter is ingesteld op FALSE. De choice klasse bindt echter gegevens aan de gebeurtenis zelf in plaats van aan een extern synchronisatieobject.

Normaal gesproken gebruikt u de choice klasse samen met de gelijktijdigheidsfunctie::receive om de controlestroom in uw toepassing te stimuleren. Gebruik de choice klasse wanneer u moet selecteren tussen berichtbuffers met verschillende typen. Gebruik de single_assignment klasse wanneer u moet selecteren tussen berichtbuffers met hetzelfde type.

De volgorde waarin u bronnen aan een choice object koppelt, is belangrijk omdat hiermee kan worden bepaald welk bericht is geselecteerd. Denk bijvoorbeeld aan het geval dat u meerdere berichtbuffers koppelt die al een bericht aan een choice object bevatten. Het choice object selecteert het bericht uit de eerste bron waaraan het is gekoppeld. Nadat u alle bronnen hebt gekoppeld, behoudt het choice object de volgorde waarin elke bron een bericht ontvangt.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de choice klasse. In dit voorbeeld wordt de concurrency::make_choice functie gebruikt om een choice object te maken dat een keuze maakt uit drie berichtblokken. Het voorbeeld berekent vervolgens verschillende Fibonacci-getallen en slaat elk resultaat op in een ander berichtblok. In het voorbeeld wordt vervolgens een bericht op de console afgedrukt dat is gebaseerd op de bewerking die eerst is voltooid.

// 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;
   }
}

In dit voorbeeld wordt de volgende voorbeelduitvoer geproduceerd:

fib35 received its value first. Result = 9227465

Omdat de taak waarmee het35e Fibonacci-getal wordt berekend, niet als eerste wordt voltooid, kan de uitvoer van dit voorbeeld variëren.

In dit voorbeeld wordt de concurrency::parallel_invoke algoritme gebruikt om de Fibonacci-getallen parallel te berekenen. Zie parallel_invoke voor meer informatie.

Voor een volledig voorbeeld waarin wordt getoond hoe u de choice klasse kunt gebruiken, raadpleegt u Procedure: Selecteren tussen voltooide taken.

[Boven]

join- en multitype_join-klassen

Met de klassen gelijktijdigheid:::join en gelijktijdigheid::multitype_join kunt u wachten tot elk lid van een set bronnen een bericht ontvangt. De join klasse werkt op bronobjecten met een gemeenschappelijk berichttype. De multitype_join klasse werkt op bronobjecten die verschillende berichttypen kunnen hebben.

Lezen vanuit een join of multitype_join object lijkt op het aanroepen van de Windows API-functie WaitForMultipleObjects wanneer de bWaitAll parameter is ingesteld op TRUE. Net als een choice object join gebruiken objecten multitype_join echter een gebeurtenismechanisme waarmee gegevens worden gekoppeld aan de gebeurtenis zelf in plaats van aan een extern synchronisatieobject.

Lezen vanuit een join object produceert een std::vector-object . Lezen vanuit een multitype_join object produceert een std::tuple-object . Elementen worden in deze objecten weergegeven in dezelfde volgorde als de bijbehorende bronbuffers, zijn gekoppeld aan het join of multitype_join object. Omdat de volgorde waarin u bronbuffers koppelt aan een join of multitype_join object is gekoppeld aan de volgorde van elementen in het resulterende vector object tuple , wordt u aangeraden een bestaande bronbuffer niet los te koppelen van een join. Dit kan leiden tot niet-opgegeven gedrag.

Greedy versus niet-greedy koppelingen

De join en multitype_join klassen ondersteunen het concept van hebzuchtige en niet-hebzuchtige joins. Een greedy join accepteert een bericht van elke bron zodra berichten beschikbaar komen, totdat alle berichten beschikbaar zijn. Een niet-lustige join ontvangt berichten in twee fasen. Ten eerste wacht een niet-greedy join totdat hem een bericht van elk van zijn bronnen wordt aangeboden. Ten tweede, nadat alle bronberichten beschikbaar zijn, probeert een niet-hongerige join elk bericht te reserveren. Als elk bericht kan worden gereserveerd, worden alle berichten verbruikt en naar het doel ervan doorgegeven. Anders worden de berichtreserveringen vrijgegeven of geannuleerd en wordt opnieuw gewacht tot elke bron een bericht ontvangt.

Greedy joins presteren beter dan niet-greedy joins omdat ze berichten onmiddellijk accepteren. In zeldzame gevallen kunnen greedy joins echter leiden tot impasses. Gebruik een niet-greedy join wanneer u meerdere joins hebt die een of meer gedeelde bronobjecten bevatten.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de join klasse. In dit voorbeeld wordt de gelijktijdigheidsfunctie::make_join gebruikt om een join object te maken dat van drie single_assignment objecten wordt ontvangen. In dit voorbeeld worden verschillende Fibonacci-getallen berekend, elk resultaat in een ander single_assignment object opgeslagen en vervolgens afgedrukt naar de console, elk resultaat dat het join object bevat. Dit voorbeeld is vergelijkbaar met het voorbeeld voor de choice klasse, behalve dat de join klasse wacht tot alle bronberichtblokken een bericht ontvangen.

// 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;
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

fib35 = 9227465fib37 = 24157817half_of_fib42 = 1.33957e+008

In dit voorbeeld wordt de concurrency::parallel_invoke algoritme gebruikt om de Fibonacci-getallen parallel te berekenen. Zie parallel_invoke voor meer informatie.

Voor volledige voorbeelden die laten zien hoe u de join klasse gebruikt, zie Procedure: Selecteren tussen voltooide taken en Overzicht: Join gebruiken om impasses te voorkomen.

[Boven]

timerklasse

De concurrency::timer klasse fungeert als een berichtbron. Een timer object verzendt een bericht naar een doel nadat een opgegeven periode is verstreken. De timer klasse is handig wanneer u het verzenden van een bericht moet vertragen of als u een bericht met een regelmatig interval wilt verzenden.

De timer klasse verzendt het bericht naar slechts één doel. Als u de _PTarget-parameter in de constructor instelt op NULL, kunt u later het doel opgeven door de methode concurrency::ISource::link_target aan te roepen.

Een timer object kan herhalend of niet-herhalend zijn. Als u een herhalende timer wilt maken, geeft true de _Repeating parameter door wanneer u de constructor oproept. Anders geeft u false door voor de _Repeating parameter om een niet-herhalende timer te maken. Als de timer herhaalt, wordt na elk interval hetzelfde bericht naar het doel verzonden.

De agentsbibliotheek maakt timer objecten met de niet-gestarte status. Als u een timerobject wilt starten, roept u de gelijktijdigheid::timer::startmethode aan. Als u een timer object wilt stoppen, vernietigt u het object of roept u de concurrency::timer::stop methode aan. Als u een herhalende timer wilt onderbreken, roept u de concurrency::timer::pause-methode aan.

Voorbeeld

In het volgende voorbeeld ziet u de basisstructuur van het werken met de timer klasse. In het voorbeeld worden timer en call objecten gebruikt om de voortgang van een langdurige bewerking te rapporteren.

// 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;
}

In dit voorbeeld wordt de volgende voorbeelduitvoer geproduceerd:

Computing fib(42)..................................................result is 267914296

Zie timer) voor een volledig voorbeeld waarin wordt getoond hoe u de klasse kunt gebruiken.

[Boven]

Berichtfiltering

Wanneer u een berichtblokobject maakt, kunt u een filterfunctie opgeven waarmee wordt bepaald of het berichtblok een bericht accepteert of weigert. Een filterfunctie is een handige manier om te garanderen dat een berichtblok alleen bepaalde waarden ontvangt.

In het volgende voorbeeld ziet u hoe u een unbounded_buffer object maakt dat gebruikmaakt van een filterfunctie om alleen even getallen te accepteren. Het unbounded_buffer object weigert oneven getallen en geeft daarom geen oneven getallen door aan de doelblokken.

// 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;
   }
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

0 2 4 6 8

Een filterfunctie kan een lambda-functie, een functie aanwijzer of een functieobject zijn. Elke filterfunctie heeft een van de volgende formulieren.

bool (T)
bool (T const &)

Als u het onnodig kopiëren van gegevens wilt elimineren, gebruikt u het tweede formulier wanneer u een aggregaattype hebt dat wordt doorgegeven door waarde.

Berichtfiltering ondersteunt het programmeermodel voor gegevensstromen , waarin onderdelen berekeningen uitvoeren wanneer ze gegevens ontvangen. Voor voorbeelden die filterfuncties gebruiken om de stroom van gegevens in een berichtendoorgiftenetwerk te beheren, raadpleegt u Procedure: Een filter voor berichtblok gebruiken, walkthrough: een gegevensstroomagent maken en walkthrough: een Image-Processing netwerk maken.

[Boven]

Berichtenreservering

Met een berichtreservering kan een berichtblok een bericht reserveren voor later gebruik. Meestal wordt berichtreservering niet rechtstreeks gebruikt. Als u echter een berichtreservering begrijpt, kunt u beter inzicht krijgen in het gedrag van een aantal vooraf gedefinieerde typen berichtblokken.

Overweeg niet-hebzuchtige en hebzuchtige joins. Beide maken gebruik van een berichtreservering om berichten te reserveren voor later gebruik. Zoals eerder beschreven, ontvangt een niet-greedy join berichten in twee fasen. Tijdens de eerste fase wacht een niet-greedy join object tot elk van zijn bronnen een bericht ontvangt. Een niet-hebzuchtige join probeert vervolgens elk van deze berichten vast te leggen. Als elk bericht kan worden gereserveerd, worden alle berichten verbruikt en naar het doel ervan doorgegeven. Anders worden de berichtreserveringen vrijgegeven of geannuleerd en wordt opnieuw gewacht tot elke bron een bericht ontvangt.

Een greedy join, die ook invoerberichten van een aantal bronnen leest, gebruikt berichtreservering om extra berichten te lezen terwijl er wordt gewacht op het ontvangen van een bericht van elke bron. Denk bijvoorbeeld aan een greedy join die berichten ontvangt van berichtblokken A en B. Als de hebzuchtige join twee berichten van B ontvangt maar nog geen bericht Aheeft ontvangen, slaat de hebzuchtige join de unieke bericht-id op voor het tweede bericht van B. Nadat de hebzuchtige join een bericht ontvangt van A en deze berichten heeft doorgegeven, wordt de opgeslagen bericht-id gebruikt om te zien of het tweede bericht B nog steeds beschikbaar is.

U kunt berichtreservering gebruiken wanneer u uw eigen aangepaste berichtbloktypen implementeert. Zie Walkthrough voor een voorbeeld van het maken van een aangepast berichtbloktype : Een aangepast berichtblok maken.

[Boven]

Zie ook

Bibliotheek met asynchrone agents