Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Agentbiblioteket innehåller flera typer av meddelandeblock som gör att du kan sprida meddelanden mellan programkomponenter på ett trådsäkert sätt. Dessa typer av meddelandeblock används ofta med de olika rutinerna för meddelandeöverföring, till exempel samtidighet::skicka, samtidighet::asend, samtidighet::ta emot och samtidighet::try_receive. Mer information om de rutiner för meddelandeöverföring som definieras av agentbiblioteket finns i Meddelandeöverföringsfunktioner.
Sektioner
Det här avsnittet innehåller följande avsnitt:
Källor och mål
Källor och mål är två viktiga deltagare i meddelandeöverföring. En källa refererar till en slutpunkt för kommunikation som skickar meddelanden. Ett mål refererar till en slutpunkt för kommunikation som tar emot meddelanden. Du kan se en källa som en slutpunkt som du läser från och ett mål som en slutpunkt som du skriver till. Program ansluter källor och mål tillsammans för att bilda meddelandenätverk.
Agentbiblioteket använder två abstrakta klasser för att representera källor och mål: samtidighet::ISource och samtidighet::ITarget. Typer av meddelandeblock som fungerar som källor härleds från ISource; typer av meddelandeblock som fungerar som mål härleds från ITarget. Typer av meddelandeblock som fungerar som källor och mål härleds från både ISource och ITarget.
[Topp]
Meddelandespridning
              Meddelandespridning handlar om att skicka ett meddelande från en komponent till en annan. När ett meddelandeblock erbjuds ett meddelande kan det acceptera, avvisa eller skjuta upp meddelandet. Varje meddelandeblocktyp lagrar och överför meddelanden på olika sätt. Klassen lagrar till exempel unbounded_buffer ett obegränsat antal meddelanden, overwrite_buffer klassen lagrar ett enda meddelande i taget och transformatorklassen lagrar en ändrad version av varje meddelande. Dessa typer av meddelandeblock beskrivs mer detaljerat senare i det här dokumentet.
När ett meddelandeblock accepterar ett meddelande kan det också utföra arbete och, om meddelandeblocket är en källa, skicka det resulterande meddelandet till en annan medlem i nätverket. Ett meddelandeblock kan använda en filterfunktion för att avvisa meddelanden som den inte vill ta emot. Filter beskrivs mer detaljerat senare i det här avsnittet i avsnittet Meddelandefiltrering. Ett meddelandeblock som skjuter upp ett meddelande kan reservera meddelandet och använda det senare. Meddelandereservation beskrivs mer detaljerat senare i det här avsnittet i avsnittet Meddelandereservation.
Agentbiblioteket gör det möjligt för meddelandeblock att asynkront eller synkront skicka meddelanden. När du skickar ett meddelande till ett meddelandeblock synkront, till exempel genom att använda send funktionen, blockerar körningen den aktuella kontexten tills målblocket antingen accepterar eller avvisar meddelandet. När du skickar ett meddelande till ett meddelandeblock asynkront, till exempel genom att använda asend funktionen, erbjuder körningen meddelandet till målet, och om målet accepterar meddelandet schemalägger körningen en asynkron uppgift som sprider meddelandet till mottagaren. Körningen använder lätta uppgifter för att sprida meddelanden på ett samarbetsmässigt sätt. Mer information om enklare uppgifter finns i Schemaläggaren.
Program ansluter källor och mål tillsammans för att bilda meddelandenätverk. Vanligtvis länkar du nätverket och anropar send eller asend för att skicka data till nätverket. Om du vill ansluta ett källmeddelandeblock till ett mål anropar du metoden concurrency::ISource::link_target . Koppla bort ett källblock från ett mål genom att anropa metoden concurrency::ISource::unlink_target. Om du vill koppla bort ett källblock från alla dess mål anropar du metoden concurrency::ISource::unlink_targets . När en av de fördefinierade meddelandeblocktyperna lämnar omfånget eller förstörs, kopplas den automatiskt bort från alla målblock. Vissa typer av meddelandeblock begränsar det maximala antalet mål som de kan skriva till. I följande avsnitt beskrivs de begränsningar som gäller för de fördefinierade typerna av meddelandeblock.
[Topp]
Översikt över typer av meddelandeblock
I följande tabell beskrivs kortfattat rollen för de viktiga typerna av meddelandeblock.
              unbounded_buffer
Lagrar en kö med meddelanden.
              skriv över buffert
Lagrar ett meddelande som kan skrivas till och läsas från flera gånger.
              single_assignment
Lagrar ett meddelande som kan skrivas till en gång och läsas från flera gånger.
              anropa
Utför arbete när det tar emot ett meddelande.
              transformator
Utför arbete när det tar emot data och skickar resultatet av arbetet till ett annat målblock. Klassen transformer kan fungera på olika typer av indata och utdata.
              val
Väljer det första tillgängliga meddelandet från en uppsättning källor.
              anslutning och multitype-anslutning
Vänta tills alla meddelanden tas emot från en uppsättning källor och kombinera sedan meddelandena till ett meddelande för ett annat meddelandeblock.
              timer
Skickar ett meddelande till ett målblock med ett regelbundet intervall.
Dessa typer av meddelandeblock har olika egenskaper som gör dem användbara för olika situationer. Det här är några av egenskaperna:
- Spridningstyp: Om meddelandeblocket fungerar som en datakälla, en mottagare av data eller både och. 
- Meddelandeordning: Om meddelandeblocket upprätthåller den ursprungliga ordningen i vilken meddelanden skickas eller tas emot. Varje fördefinierad typ av meddelandeblock upprätthåller den ursprungliga ordningen i vilken den skickar eller tar emot meddelanden. 
- Antal källor: Det maximala antalet källor som meddelandeblocket kan läsa från. 
- Antal mål: Det maximala antalet mål som meddelandeblocket kan skriva till. 
I följande tabell visas hur dessa egenskaper relaterar till de olika typerna av meddelandeblock.
| Typ av meddelandeblock | Spridningstyp (källa, mål eller båda) | Meddelandeordning (ordnad eller osorterad) | Antal källor | Antal målobjekt | 
|---|---|---|---|---|
| unbounded_buffer | Båda | Beställt | Obegränsad | Obegränsad | 
| overwrite_buffer | Båda | Beställt | Obegränsad | Obegränsad | 
| single_assignment | Båda | Beställt | Obegränsad | Obegränsad | 
| call | Mål | Beställt | Obegränsad | Ej tillämpligt | 
| transformer | Båda | Beställt | Obegränsad | 1 | 
| choice | Båda | Beställt | 10 | 1 | 
| join | Båda | Beställt | Obegränsad | 1 | 
| multitype_join | Båda | Beställt | 10 | 1 | 
| timer | Källa | Ej tillämpligt | Ej tillämpligt | 1 | 
I följande avsnitt beskrivs typerna av meddelandeblock mer detaljerat.
[Topp]
klass av typen unbounded_buffer
              Klassen concurrency::unbounded_buffer representerar en asynkron meddelandestruktur för generell användning. Den här klassen lagrar en kö av typen först in, först ut (FIFO) som kan skrivas till av flera källor eller läsas från av flera mål. När ett mål tar emot ett meddelande från ett unbounded_buffer objekt tas meddelandet bort från meddelandekön. Även om ett unbounded_buffer objekt kan ha flera mål får därför bara ett mål varje meddelande. Klassen unbounded_buffer är användbar när du vill skicka flera meddelanden till en annan komponent och komponenten måste ta emot varje meddelande.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med unbounded_buffer-klassen. Det här exemplet skickar tre värden till ett unbounded_buffer objekt och läser sedan tillbaka dessa värden från samma objekt.
// 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;
}
Det här exemplet genererar följande utdata:
334455
Ett fullständigt exempel som visar hur du använder klassen finns i unbounded_bufferSå här: Implementera olika Producer-Consumer mönster.
[Topp]
overwrite_buffer-klass
              Klassen concurrency::overwrite_buffer liknar unbounded_buffer klassen, förutom att ett overwrite_buffer objekt bara lagrar ett meddelande. När ett mål tar emot ett meddelande från ett overwrite_buffer objekt tas meddelandet dessutom inte bort från bufferten. Därför får flera mål en kopia av meddelandet.
Klassen overwrite_buffer är användbar när du vill skicka flera meddelanden till en annan komponent, men komponenten behöver bara det senaste värdet. Den här klassen är också användbar när du vill sända ett meddelande till flera komponenter.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med overwrite_buffer-klassen. Det här exemplet skickar tre värden till ett overwrite _buffer objekt och läser sedan det aktuella värdet från samma objekt tre gånger. Det här exemplet liknar exemplet för unbounded_buffer klassen. Klassen lagrar dock overwrite_buffer bara ett meddelande. Dessutom tar körningsmiljön inte bort meddelandet från ett overwrite_buffer objekt efter att det blivit läst.
// 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;
}
Det här exemplet genererar följande utdata:
555555
Ett fullständigt exempel som visar hur du använder klassen finns i overwrite_bufferSå här: Implementera olika Producer-Consumer mönster.
[Topp]
klass för enkel tilldelning
              Concurrency::single_assignment-klassen liknar overwrite_buffer klassen, förutom att ett single_assignment objekt endast kan skrivas till en gång. Precis som i klassen overwrite_buffer tas meddelandet inte bort från objektet när ett mål tar emot ett meddelande från ett single_assignment-objekt. Därför får flera mål en kopia av meddelandet. Klassen single_assignment är användbar när du vill sända ett meddelande till flera komponenter.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med single_assignment-klassen. Det här exemplet skickar tre värden till ett single_assignment objekt och läser sedan det aktuella värdet från samma objekt tre gånger. Det här exemplet liknar exemplet för overwrite_buffer klassen. Även om både klasserna overwrite_buffer och single_assignment lagrar ett enda meddelande kan single_assignment klassen endast skrivas till en gång.
// 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;
}
Det här exemplet genererar följande utdata:
333333
För ett komplett exempel som visar hur du använder klassen single_assignment, se Arbetsgång: Implementera Futures.
[Topp]
anropa klass
              Concurrency::call-klassen fungerar som en meddelandemottagare som utför en arbetsfunktion när den tar emot data. Den här arbetsfunktionen kan vara ett lambda-uttryck, ett funktionsobjekt eller en funktionspekare. Ett call objekt fungerar annorlunda än ett vanligt funktionsanrop eftersom det fungerar parallellt med andra komponenter som skickar meddelanden till det. Om ett call objekt utför arbete när det tar emot ett meddelande läggs meddelandet till i en kö. Varje call objekt bearbetar köade meddelanden i den ordning de tas emot.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med call-klassen. I det här exemplet skapas ett call objekt som skriver ut varje värde som det tar emot till konsolen. Exemplet skickar sedan tre värden till objektet call . 
              call Eftersom objektet bearbetar meddelanden i en separat tråd använder det här exemplet även en räknarvariabel och ett händelseobjekt för att säkerställa att call objektet bearbetar alla meddelanden innan wmain funktionen returneras.
// 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();
}
Det här exemplet genererar följande utdata:
334455
Ett fullständigt exempel som visar hur du använder klassen finns i callSå här: Tillhandahålla arbetsfunktioner till anrops- och transformeringsklasserna.
[Topp]
transformeringsklass
Klassen concurrency::transformer fungerar både som en meddelandemottagare och en meddelandesändare. Klassen transformer liknar klassen call eftersom den utför en användardefinierad arbetsfunktion när den tar emot data. Klassen skickar dock transformer också resultatet av arbetsfunktionen till mottagarobjekt. Precis som ett call objekt fungerar ett transformer objekt parallellt med andra komponenter som skickar meddelanden till det. Om ett transformer objekt utför arbete när det tar emot ett meddelande läggs meddelandet till i en kö. Varje transformer objekt bearbetar sina köade meddelanden i den ordning de tas emot.
Klassen transformer skickar sitt meddelande till ett mål. Om du anger parametern _PTarget i konstruktorn till NULLkan du senare ange målet genom att anropa metoden concurrency::link_target .
Till skillnad från alla andra asynkrona typer av meddelandeblock som tillhandahålls av agentbiblioteket transformer kan klassen agera på olika typer av indata och utdata. Den här möjligheten att transformera data från en typ till en annan gör transformer klassen till en nyckelkomponent i många samtidiga nätverk. Dessutom kan du lägga till mer detaljerade parallella funktioner i arbetsfunktionen för ett transformer objekt.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med transformer-klassen. Det här exemplet skapar ett transformer objekt som multiplar varje indatavärde int med 0,33 för att skapa ett double värde som utdata. Exemplet tar sedan emot transformerade värden från samma transformer objekt och skriver ut dem till konsolen.
// 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;
}
Det här exemplet genererar följande utdata:
10.8914.5218.15
Ett fullständigt exempel som visar hur du transformer använder klassen finns i Så här använder du transformering i en datapipeline.
[Topp]
valklass
Klassen concurrency::choice väljer det första tillgängliga meddelandet från en uppsättning källor. Klassen choice representerar en kontrollflödesmekanism i stället för en dataflödesmekanism (ämnet Asynkront agentbibliotek beskriver skillnaderna mellan dataflöde och kontrollflöde).
Att läsa från ett valobjekt liknar att anropa Windows API-funktionen WaitForMultipleObjects när parametern bWaitAll är inställd på FALSE. Klassen binder dock choice data till själva händelsen i stället för till ett externt synkroniseringsobjekt.
Vanligtvis använder choice du klassen tillsammans med funktionen concurrency::receive för att driva kontrollflöde i ditt program. 
              choice Använd klassen när du måste välja bland meddelandebuffertar som har olika typer. 
              single_assignment Använd klassen när du måste välja bland meddelandebuffertar som har samma typ.
I vilken ordning du länkar källor till ett choice objekt är det viktigt eftersom det kan avgöra vilket meddelande som har valts. Tänk till exempel på det fall där du länkar flera meddelandebuffertar som redan innehåller ett meddelande till ett choice objekt. Objektet choice väljer meddelandet från den första källan som det är länkat till. När du har länkat alla källor choice bevarar objektet den ordning i vilken varje källa tar emot ett meddelande.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med choice-klassen. I det här exemplet används funktionen concurrency::make_choice för att skapa ett choice objekt som väljs bland tre meddelandeblock. Exemplet beräknar sedan olika Fibonacci-tal och lagrar varje resultat i ett annat meddelandeblock. Exemplet skriver sedan ut ett meddelande till konsolen som baseras på den åtgärd som slutfördes först.
// 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;
   }
}
I det här exemplet skapas följande exempelutdata:
fib35 received its value first. Result = 9227465
Eftersom uppgiften som beräknar det 35:e Fibonacci-talet inte garanteras att slutföras först kan utdata i det här exemplet variera.
I det här exemplet används algoritmen concurrency::p arallel_invoke för att beräkna Fibonacci-talen parallellt. Mer information om parallel_invokefinns i Parallella algoritmer.
Ett fullständigt exempel som visar hur du använder klassen finns i choiceHow to: Select Among Completed Tasks (Så här gör du: Välj bland slutförda aktiviteter).
[Topp]
gå med i och multitype_join klasser
              Med klasserna concurrency::join och concurrency::multitype_join kan du vänta tills varje medlem i en uppsättning källor får ett meddelande. Klassen join fungerar på källobjekt som har en gemensam meddelandetyp. Klassen multitype_join fungerar på källobjekt som kan ha olika meddelandetyper.
Läsning från ett join eller multitype_join -objekt liknar att anropa Funktionen Windows API WaitForMultipleObjects när parametern är inställd på bWaitAllTRUE. Men precis som ett choice objekt join och multitype_join objekt använder en händelsemekanism som binder data till själva händelsen i stället för till ett externt synkroniseringsobjekt.
Läsning från ett join objekt ger ett std::vector-objekt . När du läser från ett multitype_join objekt skapas ett std::tuple-objekt. Element visas i dessa objekt i samma ordning som deras motsvarande källbuffertar är länkade till join objektet eller multitype_join . Eftersom den ordning i vilken du länkar källbuffertar till ett join eller multitype_join -objekt är associerad med ordningen på elementen i det resulterande vector objektet eller tuple objektet rekommenderar vi att du inte avlänkar en befintlig källbuffert från en koppling. Detta kan leda till ospecificerat beteende.
Giriga kontra icke-giriga kopplingar
Klasserna join och multitype_join stöder begreppet giriga och icke-giriga sammankopplingar. En girig koppling accepterar ett meddelande från var och en av dess källor när meddelanden blir tillgängliga tills alla meddelanden är tillgängliga. En icke-girig koppling tar emot meddelanden i två faser. För det första väntar en icke-girig anslutning tills den erbjuds ett meddelande från var och en av sina källor. För det andra, när alla källmeddelanden är tillgängliga, försöker en icke-girig koppling att reservera vart och ett av dessa meddelanden. Om det kan reservera varje meddelande förbrukar det alla meddelanden och sprider dem till målet. Annars frigörs eller avbryts meddelandereservationerna och väntar igen på att varje källa ska ta emot ett meddelande.
Giriga kopplingar presterar bättre än icke-giriga kopplingar eftersom de accepterar meddelanden omedelbart. Men i sällsynta fall kan giriga sammanfogningar leda till dödlägen. Använd en icke-girig koppling när du har flera kopplingar som innehåller ett eller flera delade källobjekt.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med join-klassen. I det här exemplet används funktionen concurrency::make_join för att skapa ett join objekt som tar emot från tre single_assignment objekt. Det här exemplet beräknar olika Fibonacci-tal, lagrar varje resultat i ett annat single_assignment objekt och skriver sedan ut till konsolen varje resultat som join objektet innehåller. Det här exemplet liknar exemplet för choice klassen, förutom att klassen väntar på att join alla källmeddelandeblock ska ta emot ett meddelande.
// 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;
}
Det här exemplet genererar följande utdata:
fib35 = 9227465fib37 = 24157817half_of_fib42 = 1.33957e+008
I det här exemplet används algoritmen concurrency::p arallel_invoke för att beräkna Fibonacci-talen parallellt. Mer information om parallel_invokefinns i Parallella algoritmer.
För fullständiga exempel som visar hur du använder join-klassen, se Så här: Välj bland slutförda uppgifter och Genomgång: Använda join för att förhindra dödläge.
[Topp]
timerklass
              Klassen concurrency::timer fungerar som en meddelandekälla. Ett timer objekt skickar ett meddelande till ett mål efter att en angiven tidsperiod har förflutit. Klassen timer är användbar när du måste fördröja sändningen av ett meddelande eller om du vill skicka ett meddelande med jämna mellanrum.
Klassen timer skickar sitt meddelande till bara ett mål. Om du anger parametern _PTarget i konstruktorn till NULLkan du senare ange målet genom att anropa metoden concurrency::ISource::link_target .
Ett timer objekt kan upprepas eller inte upprepas. Om du vill skapa en timer som upprepas, ange true för parametern _Repeating när du anropar konstruktorn. Annars ange false som värde för parametern _Repeating för att skapa en icke-upprepande timer. Om timern upprepas skickar den samma meddelande till målet efter varje intervall.
Agentbiblioteket skapar timer objekt i icke-startat tillstånd. Starta ett tidsinställt objekt genom att anropa metoden concurrency::timer::start . Om du vill stoppa ett timer objekt förstör du objektet eller anropar metoden concurrency::timer::stop . Om du vill pausa en upprepad timer, anropa metoden concurrency::timer::pause.
Exempel
Följande exempel visar den grundläggande strukturen för hur du arbetar med timer-klassen. Exemplet använder timer och call objekt för att rapportera förloppet för en lång åtgärd.
// 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;
}
I det här exemplet skapas följande exempelutdata:
Computing fib(42)..................................................result is 267914296
Ett fullständigt exempel som visar hur du använder klassen finns i timerHow to: Send a Message at a Regular Interval (Så här skickar du ett meddelande med ett regelbundet intervall).
[Topp]
Meddelandefiltrering
När du skapar ett objekt för meddelandeblock kan du ange en filterfunktion som avgör om meddelandeblocket accepterar eller avvisar ett meddelande. En filterfunktion är ett användbart sätt att garantera att ett meddelandeblock endast tar emot vissa värden.
I följande exempel visas hur du skapar ett unbounded_buffer objekt som använder en filterfunktion för att endast acceptera jämna tal. Objektet unbounded_buffer avvisar udda tal och sprider därför inte udda tal till sina målblock.
// 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;
   }
}
Det här exemplet genererar följande utdata:
0 2 4 6 8
En filterfunktion kan vara en lambda-funktion, en funktionspekare eller ett funktionsobjekt. Varje filterfunktion använder något av följande formulär.
bool (T)
bool (T const &)
Om du vill eliminera onödig kopiering av data använder du det andra formuläret när du har en aggregeringstyp som sprids efter värde.
Meddelandefiltrering stöder programmeringsmodellen för dataflöden , där komponenter utför beräkningar när de tar emot data. Exempel som använder filterfunktioner för att styra dataflödet i ett meddelandeöverförande nätverk finns i Använda ett meddelandeblockfilter, Genomgång: Skapa en dataflödesagent och Genomgång: Skapa ett Image-Processing nätverk.
[Topp]
Meddelandereservation
Med meddelandereservation kan ett meddelandeblock reservera ett meddelande för senare användning. Vanligtvis används inte meddelandereservationen direkt. Att förstå meddelandereservation kan dock hjälpa dig att bättre förstå beteendet för vissa av de fördefinierade typerna av meddelandeblock.
Överväg icke-giriga och giriga sammanfogningar. Båda dessa använder meddelandereservation för att reservera meddelanden för senare användning. En tidigare beskrivning är att en icke-girig koppling tar emot meddelanden i två faser. Under den första fasen väntar ett icke-girigt join objekt på att var och en av dess källor ska få ett meddelande. En icke-girig sammanslagning försöker sedan reservera vart och ett av dessa meddelanden. Om det kan reservera varje meddelande förbrukar det alla meddelanden och sprider dem till målet. Annars frigörs eller avbryts meddelandereservationerna och väntar igen på att varje källa ska ta emot ett meddelande.
En girig koppling, som också läser indatameddelanden från ett antal källor, använder meddelandereservation för att läsa ytterligare meddelanden medan den väntar på att få ett meddelande från varje källa. Tänk dig till exempel en girig koppling som tar emot meddelanden från meddelandeblock A och B. Om den giriga kopplingen tar emot två meddelanden från B men ännu inte har fått något meddelande från Asparar den giriga kopplingen den unika meddelandeidentifieraren för det andra meddelandet från B. När den giriga kopplingen tar emot ett meddelande från A och sprider ut dessa meddelanden använder den den sparade meddelandeidentifieraren för att se om det andra meddelandet från B fortfarande är tillgängligt.
Du kan använda meddelandereservation när du implementerar dina egna typer av anpassade meddelandeblock. Ett exempel på hur du skapar en typ av anpassat meddelandeblock finns i Genomgång: Skapa ett anpassat meddelandeblock.
[Topp]