Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
La bibliothèque Agents fournit plusieurs types de blocs de messages qui vous permettent de propager des messages entre les composants d'une application de manière thread-safe. Ces types de blocs de messages sont souvent utilisés avec différentes routines de passage de messages, telles que concurrency::send, concurrency::asend, concurrency::receive, et concurrency::try_receive. Pour plus d’informations sur les routines de passage de messages définies par la bibliothèque Agents, veuillez consulter la section Fonctions de passage de messages.
Rubriques
Cette rubrique contient les sections suivantes :
Sources et cibles
Les sources et les cibles sont deux participants importants du passage de messages. Une source désigne un point de terminaison de communication qui envoie des messages. Une cible désigne un point de terminaison de communication qui reçoit des messages. Vous pouvez considérer une source comme un point de terminaison à partir duquel vous lisez, et une cible comme un point de terminaison vers lequel vous écrivez. Les applications connectent les sources et les cibles entre elles pour former des réseaux de messagerie.
La bibliothèque Agents utilise deux classes abstraites pour représenter les sources et les cibles : concurrency::ISource et concurrency::ITarget. Les types de blocs de messages agissant en tant que sources dérivent de ISource ; les types de blocs de messages agissant en tant que cibles dérivent de ITarget. Les types de blocs de messages agissant en tant que sources et cibles dérivent à la fois de ISource et ITarget.
[Haut]
Propagation des messages
La propagation des messages est l’action d’envoyer un message d’un composant à un autre. Lorsqu’un bloc de messages reçoit un message, il peut accepter, refuser ou reporter ce message. Chaque type de bloc de messages stocke et transmet les messages de manière différente. Par exemple, la classe unbounded_buffer stocke un nombre illimité de messages, la classe overwrite_buffer stocke un seul message à la fois et la classe transformer stocke une version modifiée de chaque message. Ces types de blocs de messages sont décrits plus en détail plus loin dans ce document.
Lorsqu’un bloc de messages accepte un message, il peut éventuellement effectuer une opération et, s’il s’agit d’une source, transmettre le message résultant à un autre membre du réseau. Un bloc de messages peut utiliser une fonction de filtrage pour refuser les messages qu’il ne souhaite pas recevoir. Les filtres sont décrits plus en détail plus loin dans cette rubrique, dans la section Filtrage des messages. Un bloc de messages qui reporte un message peut le réserver et le consommer ultérieurement. La réservation des messages est décrite plus en détail plus loin dans cette rubrique, dans la section Réservation des messages.
La bibliothèque Agents permet aux blocs de messages de transmettre des messages de manière asynchrone ou synchrone. Lorsque vous transmettez un message à un bloc de messages de façon synchrone, par exemple à l’aide de la fonction send, le runtime bloque le contexte actuel jusqu’à ce que le bloc cible accepte ou rejette le message. Lorsque vous transmettez un message à un bloc de messages de façon asynchrone, par exemple à l’aide de la fonction asend, le runtime propose le message à la cible, et si celle-ci accepte le message, il planifie une tâche asynchrone pour propager le message au récepteur. Le runtime utilise des tâches légères pour propager les messages de manière coopérative. Pour plus d’informations sur les tâches légères, veuillez consulter la section Planificateur de tâches.
Les applications connectent les sources et les cibles entre elles pour former des réseaux de messagerie. En général, vous reliez le réseau et appelez send ou asend pour transmettre des données au réseau. Pour connecter un bloc de messages source à une cible, appelez la méthode concurrency::ISource::link\_target. Pour déconnecter un bloc source d’une cible, appelez la méthode concurrency::ISource::unlink\_target. Pour déconnecter un bloc source de toutes ses cibles, appelez la méthode concurrency::ISource::unlink\_targets. Lorsqu’un des types de blocs de messages prédéfinis sort de l’étendue ou est détruit, il se déconnecte automatiquement de tous les blocs cibles. Certains types de blocs de messages limitent le nombre maximal de cibles auxquelles ils peuvent écrire. La section suivante décrit les restrictions qui s’appliquent aux types de blocs de messages prédéfinis.
[Haut]
Vue d’ensemble des types de blocs de messages
Le tableau suivant décrit brièvement le rôle des principaux types de blocs de messages.
unbounded_buffer
Stocke une file de messages.
overwrite_buffer
Stocke un message pouvant être lu et écrit plusieurs fois.
single_assignment
Stocke un message pouvant être écrit une seule fois et lu plusieurs fois.
call
Effectue une opération lorsqu’il reçoit un message.
transformer
Effectue une opération lorsqu’il reçoit des données et envoie le résultat de cette opération à un autre bloc cible. La classe transformer peut agir sur différents types d’entrée et de sortie.
choice
Sélectionne le premier message disponible parmi un ensemble de sources.
join et multitype join
Attend que tous les messages soient reçus à partir d’un ensemble de sources, puis combine ces messages en un seul message destiné à un autre bloc de messages.
timer
Envoie un message à un bloc cible à intervalle régulier.
Ces types de blocs de messages présentent des caractéristiques différentes qui les rendent utiles selon les situations. Voici certaines de ces caractéristiques :
Type de propagation : indique si le bloc de messages agit comme source de données, récepteur de données ou les deux.
Ordre des messages : indique si le bloc de messages conserve l’ordre original d’envoi ou de réception des messages. Chaque type de bloc de messages prédéfini conserve l’ordre original dans lequel il envoie ou reçoit les messages.
Nombre de sources : nombre maximal de sources dont le bloc de messages peut lire les données.
Nombre de cibles : nombre maximal de cibles auxquelles le bloc de messages peut écrire.
Le tableau suivant montre comment ces caractéristiques s’appliquent aux différents types de blocs de messages.
| Type de bloc de messages | Type de propagation (source, cible ou les deux) | Ordre des messages (ordonné ou non ordonné) | Nombre de sources | Nombre de cibles |
|---|---|---|---|---|
unbounded_buffer |
Les deux | Commandée | Non lié | Non lié |
overwrite_buffer |
Les deux | Commandée | Non lié | Non lié |
single_assignment |
Les deux | Commandée | Non lié | Non lié |
call |
Cible | Commandée | Non lié | Sans objet |
transformer |
Les deux | Commandée | Non lié | 1 |
choice |
Les deux | Commandée | 10 | 1 |
join |
Les deux | Commandée | Non lié | 1 |
multitype_join |
Les deux | Commandée | 10 | 1 |
timer |
Origine | Sans objet | Sans objet | 1 |
Les sections suivantes décrivent les types de blocs de messages plus en détail.
[Haut]
Classe unbounded_buffer
La classe concurrency::unbounded\_buffer représente une structure de messagerie asynchrone polyvalente. Cette classe stocke une file d'attente de messages de type premier entré, premier sorti (FIFO). Plusieurs cibles peuvent lire ces messages et plusieurs sources peuvent y écrire. Lorsqu’un bloc cible reçoit un message depuis un objet unbounded_buffer, ce message est supprimé de la file de messages. Par conséquent, bien qu’un objet unbounded_buffer puisse avoir plusieurs cibles, un seul bloc cible recevra chaque message. La classe unbounded_buffer est utile quand vous voulez transmettre plusieurs messages à un autre composant et que ce composant doit recevoir chaque message.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe unbounded_buffer. Cet exemple envoie trois valeurs à un objet unbounded_buffer, puis lit ces valeurs à partir du même objet.
// 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;
}
Cet exemple produit la sortie suivante :
334455
Pour un exemple complet montrant comment utiliser la classe unbounded_buffer, veuillez consulter la section Procédure : implémenter différents modèles producteur-consommateur.
[Haut]
Classe overwrite_buffer
La classe concurrency::overwrite\_buffer ressemble à la classe unbounded_buffer, sauf qu’un objet overwrite_buffer ne stocke qu’un seul message. De plus, lorsqu’un bloc cible reçoit un message d’un objet overwrite_buffer, ce message n’est pas supprimé du tampon. Par conséquent, plusieurs cibles reçoivent une copie du message.
La classe overwrite_buffer est utile lorsque vous souhaitez transmettre plusieurs messages à un autre composant, mais que celui-ci a uniquement besoin de la valeur la plus récente. Cette classe est également utile quand vous voulez diffuser un message vers plusieurs composants.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe overwrite_buffer. Cet exemple envoie trois valeurs à un objet overwrite _buffer, puis lit la valeur actuelle du même objet trois fois. Cet exemple est similaire à celui de la classe unbounded_buffer. Cependant, la classe overwrite_buffer ne stocke qu’un seul message. De plus, l’environnement d’exécution ne supprime pas le message d’un objet overwrite_buffer après sa lecture.
// 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;
}
Cet exemple produit la sortie suivante :
555555
Pour un exemple complet montrant comment utiliser la classe overwrite_buffer, veuillez consulter la section Procédure : implémenter différents modèles producteur-consommateur.
[Haut]
Classe single_assignment
La classe concurrency::single\_assignment ressemble à la classe overwrite_buffer, sauf qu’un objet single_assignment ne peut être écrit qu’une seule fois. Comme pour la classe overwrite_buffer, quand une cible reçoit un message d'un objet single_assignment, le message n'est pas supprimé de l'objet. Par conséquent, plusieurs cibles reçoivent une copie du message. La classe single_assignment est utile lorsque vous souhaitez diffuser un message à plusieurs composants.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe single_assignment. Cet exemple envoie trois valeurs à un objet single_assignment, puis lit la valeur actuelle du même objet trois fois. Cet exemple est similaire à celui de la classe overwrite_buffer. Bien que les classes overwrite_buffer et single_assignment stockent un seul message, la classe single_assignment ne peut être écrite qu’une seule fois.
// 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;
}
Cet exemple produit la sortie suivante :
333333
Pour un exemple complet montrant comment utiliser la classe single_assignment, veuillez consulter la section Présentation : implémentation d'objets Future.
[Haut]
call, classe
La classe concurrency::call agit comme un récepteur de messages qui exécute une fonction de traitement lorsqu’elle reçoit des données. Cette fonction de traitement peut être une expression lambda, un objet fonction ou un pointeur de fonction. Un objet call se comporte différemment d’un appel de fonction ordinaire, car il agit en parallèle avec d’autres composants qui lui envoient des messages. Si un objet call effectue une opération lorsqu’il reçoit un message, il ajoute ce message à une file d’attente. Chaque objet call traite les messages mis en file d’attente dans l’ordre dans lequel ils sont reçus.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe call. Cet exemple crée un objet call qui affiche chaque valeur reçue dans la console. L’exemple envoie ensuite trois valeurs à l’objet call. Comme l’objet call traite les messages sur un thread distinct, cet exemple utilise également une variable compteur et un objet event pour garantir que l’objet call traite tous les messages avant que la fonction wmain ne retourne.
// 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();
}
Cet exemple produit la sortie suivante :
334455
Pour un exemple complet montrant comment utiliser la classe call, veuillez consulter la section Procédure : fournir des fonctions de traitement aux classes call et transformer.
[Haut]
Classe transformer
La classe concurrency::transformer agit à la fois comme récepteur et comme émetteur de messages. La classe transformer ressemble à la classe call car elle exécute une fonction de traitement définie par l’utilisateur lorsqu’elle reçoit des données. Cependant, la classe transformer transmet également le résultat de cette fonction de traitement à des objets récepteurs. Comme un objet call, un objet transformer fonctionne en parallèle avec d’autres composants qui lui envoient des messages. Si un objet transformer effectue une opération lorsqu’il reçoit un message, il ajoute ce message à une file d’attente. Chaque objet transformer traite les messages mis en file d’attente dans l’ordre dans lequel ils sont reçus.
La classe transformer envoie son message à une seule cible. Si vous définissez le paramètre _PTarget dans le constructeur sur NULL, vous pouvez ensuite spécifier la cible en appelant la méthode concurrency::link\_target.
Contrairement à tous les autres types de blocs de messages asynchrones fournis par la bibliothèque Agents, la classe transformer peut agir sur des types d’entrée et de sortie différents. Cette capacité à transformer des données d’un type à un autre fait de la classe transformer un composant clé de nombreux réseaux concurrents. De plus, vous pouvez ajouter des fonctionnalités parallèles plus fines dans la fonction de traitement d’un objet transformer.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe transformer. Cet exemple crée un objet transformer qui multiplie chaque valeur d’entrée int par 0,33 pour produire une valeur double en sortie. L’exemple reçoit ensuite les valeurs transformées depuis le même objet transformer et les affiche dans la 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;
}
Cet exemple produit la sortie suivante :
10.8914.5218.15
Pour un exemple complet montrant comment utiliser la classe transformer, veuillez consulter la section Procédure : utiliser transformer dans un pipeline de données.
[Haut]
Classe choice
La classe concurrency::choice sélectionne le premier message disponible parmi un ensemble de sources. La classe choice représente un mécanisme de contrôle de flux plutôt qu’un mécanisme de flux de données (la rubrique Bibliothèque d’agents asynchrones décrit les différences entre flux de données et contrôle de flux).
Lire un objet choice revient à appeler la fonction API Windows WaitForMultipleObjects lorsque son paramètre bWaitAll est défini sur FALSE. Cependant, la classe choice associe les données à l’événement lui-même plutôt qu’à un objet de synchronisation externe.
En général, vous utilisez la classe choice avec la fonction concurrency::receive pour piloter le contrôle de flux dans votre application. Utilisez la classe choice lorsque vous devez choisir entre des tampons de messages de types différents. Utilisez la classe single_assignment lorsque vous devez choisir entre des tampons de messages de même type.
L’ordre dans lequel vous liez les sources à un objet choice est important, car il peut déterminer quel message sera sélectionné. Par exemple, supposons que vous liez plusieurs tampons de messages qui contiennent déjà un message à un objet choice. L’objet choice sélectionne le message provenant de la première source à laquelle il est lié. Une fois toutes les sources liées, l’objet choice conserve l’ordre dans lequel chaque source reçoit un message.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe choice. Cet exemple utilise la fonction concurrency::make\_choice pour créer un objet choice qui sélectionne parmi trois blocs de messages. L’exemple calcule ensuite divers nombres de Fibonacci et stocke chaque résultat dans un bloc de messages différent. Il affiche ensuite dans la console un message basé sur l’opération qui s’est terminée en premier.
// 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;
}
}
Cet exemple produit l'exemple de sortie suivant :
fib35 received its value first. Result = 9227465
Comme la tâche qui calcule le 35e nombre de Fibonacci n’est pas garantie d’être la première à se terminer, le résultat de cet exemple peut varier.
Cet exemple utilise l’algorithme concurrency::parallel\_invoke pour calculer les nombres de Fibonacci en parallèle. Pour plus d’informations sur parallel_invoke, veuillez consulter la section Algorithmes parallèles.
Pour un exemple complet montrant comment utiliser la classe choice, veuillez consulter la section Procédure : sélectionner parmi les tâches terminées.
[Haut]
Classe join and multitype_join
Les classes concurrency::join et concurrency::multitype\_join vous permettent d’attendre que chaque membre d’un ensemble de sources reçoive un message. La classe join agit sur des objets source ayant un type de message commun. La classe multitype_join agit sur des objets source pouvant avoir des types de message différents.
Lire à partir d’un objet join ou multitype_join revient à appeler la fonction API Windows WaitForMultipleObjects lorsque son paramètre bWaitAll est défini sur TRUE. Toutefois, comme un objet choice, les objets join et multitype_join utilisent un mécanisme d’événement qui lie les données à l’événement lui-même, plutôt qu’à un objet de synchronisation externe.
La lecture à partir d’un join objet produit un objet std ::vector . La lecture à partir d’un multitype_join objet produit un objet std ::tuple . Les éléments apparaissent dans ces objets dans le même ordre que celui dans lequel leurs tampons sources correspondants sont liés à l’objet join ou multitype_join. Comme l’ordre dans lequel vous liez les tampons source à un objet join ou multitype_join détermine l’ordre des éléments dans l’objet vector ou tuple résultant, nous vous recommandons de ne pas délier un tampon source existant d’un join. Cela pourrait entraîner un comportement non spécifié.
Joins gourmands (Greedy Joins) et non gourmands (Non-Greedy Joins)
Les classes join et multitype_join prennent en charge le concept de joins gourmands et non gourmands. Un join gourmand accepte un message de chacune de ses sources dès qu’ils sont disponibles, jusqu’à ce que tous les messages soient disponibles. Un join non gourmand reçoit les messages en deux phases. Premièrement, un join non gourmand attend qu’un message soit proposé par chacune de ses sources. Ensuite, une fois tous les messages source disponibles, un join non gourmand tente de réserver chacun de ces messages. S’il peut réserver chaque message, il consomme tous les messages et les transmet à sa cible. Sinon, il annule les réservations et attend à nouveau que chaque source reçoive un message.
Les joins gourmands sont plus performants que les joins non gourmands car ils acceptent immédiatement les messages. Cependant, dans de rares cas, les joins gourmands peuvent provoquer des interblocages. Utilisez un join non gourmand lorsque vous avez plusieurs joins partageant un ou plusieurs objets source.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe join. Cet exemple utilise la fonction concurrency::make\_join pour créer un objet join qui reçoit depuis trois objets single_assignment. Cet exemple calcule plusieurs nombres de Fibonacci, stocke chaque résultat dans un objet single_assignment, puis affiche dans la console chaque résultat détenu par l’objet join. Cet exemple est similaire à celui de la classe choice, à ceci près que la classe join attend que tous les blocs de messages source aient reçu un message.
// 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;
}
Cet exemple produit la sortie suivante :
fib35 = 9227465fib37 = 24157817half_of_fib42 = 1.33957e+008
Cet exemple utilise l’algorithme concurrency::parallel\_invoke pour calculer les nombres de Fibonacci en parallèle. Pour plus d’informations sur parallel_invoke, veuillez consulter la section Algorithmes parallèles.
Pour des exemples complets montrant comment utiliser la classe join, veuillez consulter les sections Procédure : sélectionner parmi les tâches terminées et Présentation : utilisation de join pour éviter les interblocages.
[Haut]
Classe timer
La classe concurrency ::timer agit en tant que source de message. Un objet timer envoie un message à une cible après un délai spécifié. La classe timer est utile lorsque vous devez différer l’envoi d’un message ou que vous souhaitez envoyer un message à intervalles réguliers.
La classe timer envoie son message à une seule cible. Si vous définissez le paramètre _PTarget dans le constructeur sur NULL, vous pouvez ensuite spécifier la cible en appelant la méthode concurrency::ISource::link\_target.
Un objet timer peut être répétitif ou non répétitif. Pour créer un minuteur répétitif, passez true comme paramètre _Repeating lors de l’appel au constructeur. Sinon, passez false comme paramètre _Repeating pour créer un minuteur non répétitif. Si le minuteur est répétitif, il envoie le même message à sa cible à chaque intervalle.
La bibliothèque Agents crée les objets timer dans un état non démarré. Pour démarrer un objet minuteur, appelez la méthode concurrency::timer::start. Pour arrêter un objet timer, détruisez l’objet ou appelez la méthode concurrency::timer::stop. Pour mettre un minuteur répétitif en pause, appelez la méthode concurrency::timer::pause.
Exemple
L’exemple suivant montre la structure de base permettant de travailler avec la classe timer. L’exemple utilise des objets timer et call pour signaler la progression d’une opération longue.
// 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;
}
Cet exemple produit l'exemple de sortie suivant :
Computing fib(42)..................................................result is 267914296
Pour un exemple complet montrant comment utiliser la classe timer, veuillez consulter la section Procédure : envoyer un message à intervalle régulier.
[Haut]
Filtrage des messages
Lorsque vous créez un objet bloc de messages, vous pouvez fournir une fonction de filtrage qui détermine si le bloc accepte ou rejette un message. Une fonction de filtrage est un moyen utile de garantir qu’un bloc de messages ne reçoive que certaines valeurs.
L’exemple suivant montre comment créer un objet unbounded_buffer qui utilise une fonction de filtrage pour n’accepter que les nombres pairs. L’objet unbounded_buffer rejette les nombres impairs et ne les propage donc pas aux blocs cibles.
// 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;
}
}
Cet exemple produit la sortie suivante :
0 2 4 6 8
Une fonction de filtrage peut être une fonction lambda, un pointeur de fonction ou un objet fonction. Chaque fonction de filtrage prend l’une des formes suivantes.
bool (T)
bool (T const &)
Pour éviter des copies de données inutiles, utilisez la seconde forme si vous avez un type agrégé propagé par valeur.
Le filtrage des messages prend en charge le modèle de programmation dataflow, dans lequel les composants effectuent des calculs lorsqu’ils reçoivent des données. Pour des exemples utilisant des fonctions de filtrage pour contrôler le flux de données dans un réseau de passage de messages, veuillez consulter les sections Procédure : utiliser un filtre de bloc de messages, Présentation : création d’un agent de flux de données et Présentation : création d’un réseau de traitement d’images.
[Haut]
Réservation des messages
La réservation des messages permet à un bloc de messages de réserver un message pour une utilisation ultérieure. En général, la réservation des messages n’est pas utilisée directement. Toutefois, comprendre ce mécanisme peut vous aider à mieux saisir le comportement de certains types de blocs de messages prédéfinis.
Considérons les joins gourmands et non gourmands. Ces deux types utilisent la réservation de messages pour conserver des messages en vue d’une utilisation ultérieure. Comme décrit précédemment, un join non gourmand reçoit les messages en deux phases. Pendant la première phase, un objet join non gourmand attend que chacune de ses sources reçoive un message. Il tente ensuite de réserver chacun de ces messages. S’il peut réserver chaque message, il consomme tous les messages et les transmet à sa cible. Sinon, il annule les réservations et attend à nouveau que chaque source reçoive un message.
Un join gourmand, qui lit également les messages d’entrée à partir de plusieurs sources, utilise la réservation de messages pour lire d’autres messages en attendant qu’un message soit reçu de chaque source. Par exemple, considérons un join gourmand qui reçoit des messages des blocs de messages A et B. Si le join gourmand reçoit deux messages de B mais n’a pas encore reçu de message de A, il enregistre l’identifiant unique du second message reçu de B. Une fois qu’il reçoit un message de A et qu’il propage les messages, il utilise l’identifiant enregistré pour vérifier si le second message de B est toujours disponible.
Vous pouvez utiliser la réservation de messages lorsque vous implémentez vos propres types personnalisés de blocs de messages. Pour voir un exemple de création d’un type personnalisé de bloc de messages, veuillez consulter la section Présentation : création d’un bloc de messages personnalisé.
[Haut]