Blocs de messages asynchrones
La bibliothèque d'agents fournit plusieurs types de blocs de messages qui vous permettent de propager des messages sur des composants d'application de manière thread-safe.Ces types de bloc de message sont souvent utilisés avec les différentes routines de transmission de messages, tels 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 d'agents, consultez Fonctions de passage de messages.
Sections
Cette rubrique contient les sections suivantes :
Sources et cibles
Propagation de messages
Vue d'ensemble des types de blocs de messages
Classe unbounded_buffer
Classe overwrite_buffer
Classe single_assignment
Classe call
Classe transformer
Classe choice
Classes join et multitype_join
Classe timer
Filtrage de messages
Réservation de messages
Sources et cibles
Les sources et les cibles sont deux participants importants au passage de message.Une source fait référence à un point de terminaison de communication qui envoie des messages.Une cible fait référence à un point de terminaison de communication qui reçoit des messages.On peut considérer une source comme un point de terminaison à partir duquel on lit et une cible comme un point de terminaison dans lequel on écrit.Les applications connectent les sources et les cibles pour former des réseaux de messagerie.
La bibliothèque d'Agents utilise deux classes abstraites pour représenter les sources et cibles : concurrency::ISource et concurrency::ITarget.Les types de blocs de messages qui jouent le rôle de sources dérivent d'ISource ; les types de blocs de messages qui jouent le rôle de cibles dérivent d' ITarget.Les types de blocs de messages qui jouent le rôle de sources et de cibles dérivent de ISource et de ITarget.
Top
Propagation de messages
La propagation de message est l'acte qui consiste à envoyer un message d'un composant à un autre.Lorsqu'un bloc de message reçoit un message, il peut l'accepter, le refuser ou le reporter.Chaque bloc de message stocke et transmet des messages de différentes façons.Par exemple, la classe unbounded_buffer stocke un nombre de messages illimité, la classe overwrite_buffer enregistre 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 ultérieurement dans ce document.
Lorsqu'un bloc de message reçoit un message, il peut éventuellement effectuer le travail et, si le bloc de message est une source, passer le message résultant à un autre membre du réseau.Un bloc de message peut utiliser une fonction de filtre pour refuser les messages qu'il ne souhaite pas recevoir.Les filtres sont décrits plus en détail ultérieurement dans cette rubrique, dans la section Filtrage de messages.Un bloc de message qui reporte un message peut réserver ce message et l'utiliser ultérieurement.La réservation de messages est décrite plus en détail ultérieurement dans cette rubrique, dans la section Réservation de messages.
La bibliothèque d'agents permet aux blocs de messages de passer des messages de façon asynchrone ou synchrone.Lorsque vous passez un message à un bloc de message 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 passez un message à un bloc de message asynchrone, par exemple à l'aide de la fonction asend, le runtime envoie un message à la cible. Si la cible l'accepte, le runtime planifie une tâche asynchrone qui propage le message au récepteur.Le runtime utilise des tâches légères pour propager des messages de manière coopérative.Pour plus d'informations sur les tâches légères, consultez Planificateur de tâches (runtime d'accès concurrentiel).
Les applications connectent les sources et les cibles pour former des réseaux de messagerie.En général, vous associez le réseau et appelez send ou asend pour passer des données au réseau.Pour connecter un bloc de message source vers une cible, appelez le concurrency::ISource::link_target méthode.Pour déconnecter un bloc de code source à partir d'une cible, appelez le concurrency::ISource::unlink_target méthode.Pour déconnecter un bloc de code source de toutes ses cibles, appelez le concurrency::ISource::unlink_targets méthode.Lorsque l'un des types de bloc de message prédéfinis quitte la portée ou qu'il 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 bloc de message prédéfinis.
Top
Vue d'ensemble des types de blocs de messages
Le tableau suivant décrit brièvement le rôle des types de blocs de messages importants.
unbounded_buffer
Stocke une file d'attente de messages.overwrite_buffer
Stocke un message qui peut être sujet à plusieurs lectures et écritures.single_assignment
Stocke un message qui peut être sujet à une seule écriture et à plusieurs lectures.appel
Exécute le travail en cas de réception d'un message.transformer
Exécute le travail en cas de réception de données et envoie le résultat de ce travail à un autre bloc cible.La classe transformer peut agir sur différents types d'entrées et de sorties.choice
Sélectionne le premier message disponible d'un jeu de sources.join et multitype join
Attendent que tous les messages soient reçus en provenance d'un jeu de sources, puis combinent les messages dans un message unique pour un autre bloc de message.timer
Envoie un message à un bloc cible à intervalle régulier.
Ces types de blocs de messages ont des caractéristiques différentes qui les rendent utiles pour différentes situations.Voici certaines de ces caractéristiques :
Type de propagation : indique si le bloc de message joue le rôle de source de données, de récepteur de données ou les deux.
Ordonnancement des messages : indique si le bloc de message conserve l'ordre d'origine dans lequel les messages sont envoyés ou reçus.Chaque type de bloc de message prédéfini conserve l'ordre d'origine dans lequel il envoie ou reçoit des messages.
Nombre de sources : nombre maximal de sources à partir desquelles le bloc de message peut lire.
Nombre de cibles : nombre maximal de cibles dans lesquelles le bloc de message peut écrire.
Le tableau suivant indique la relation entre ces caractéristiques et différents types de blocs de messages.
Type de bloc de message |
Type de propagation (Source, Cible ou Les deux) |
Ordonnancement des messages (Ordonné ou Non ordonné) |
Nombre de sources |
Nombre de cibles |
---|---|---|---|---|
unbounded_buffer |
Les deux |
Ordonné |
Illimité |
Illimité |
overwrite_buffer |
Les deux |
Ordonné |
Illimité |
Illimité |
single_assignment |
Les deux |
Ordonné |
Illimité |
Illimité |
call |
Cible |
Ordonné |
Illimité |
Non applicable |
transformer |
Les deux |
Ordonné |
Illimité |
1 |
choice |
Les deux |
Ordonné |
10 |
1 |
join |
Les deux |
Ordonné |
Illimité |
1 |
multitype_join |
Les deux |
Ordonné |
10 |
1 |
timer |
Source |
Non applicable |
Non applicable |
1 |
Les sections suivantes décrivent les types de blocs de messages de façon approfondie.
Top
Classe unbounded_buffer
Le concurrency::unbounded_buffer classe représente une structure de messagerie asynchrone universel.Cette classe stocke une file d'attente de messages « premier entré, premier sorti » (FIFO) accessible en écriture ou en lecture par plusieurs sources ou cibles.Lorsqu'une cible reçoit un message d'un objet unbounded_buffer, ce message est supprimé de la file d'attente de messages.Par conséquent, bien qu'un objet unbounded_buffer puisse avoir plusieurs cibles, une seule cible reçoit chaque message.La classe unbounded_buffer est utile lorsque vous voulez passer plusieurs messages à un autre composant et que ce composant doit recevoir chaque message.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe unbounded_buffer.Cet exemple envoie trois valeurs à un objet unbounded_buffer, puis relit 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 :
33
44
55
Pour obtenir un exemple complet qui indique comment utiliser la classe unbounded_buffer, consultez Comment : implémenter divers modèles de producteur-consommateur.
Top
Classe overwrite_buffer
Le concurrency::overwrite_buffer classe ressemble à la unbounded_buffer de classe, sauf qu'une overwrite_buffer objet stocke qu'un seul message.De plus, lorsqu'une cible reçoit un message d'un objet overwrite_buffer, ce message n'est pas supprimé de la mémoire tampon.Par conséquent, plusieurs cibles reçoivent une copie du message.
La classe overwrite_buffer est utile lorsque vous voulez passer plusieurs messages à un autre composant, mais que ce composant nécessite uniquement la valeur la plus récente.Cette classe est également utile lorsque vous voulez diffuser un message à plusieurs composants.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe overwrite_buffer.Cet exemple envoie trois valeurs à un objet overwrite _buffer, puis relit trois fois la valeur actuelle à partir du même objet.Cet exemple est similaire à l'exemple de la classe unbounded_buffer.Toutefois, la classe overwrite_buffer stocke seulement un message.En outre, le runtime ne supprime pas le message d'un objet overwrite_buffer une fois qu'il est lu.
// 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 :
55
55
55
Pour obtenir un exemple complet qui indique comment utiliser la classe overwrite_buffer, consultez Comment : implémenter divers modèles de producteur-consommateur.
Top
Classe single_assignment
Le concurrency::single_assignment classe ressemble à la overwrite_buffer de classe, sauf qu'une single_assignment objet peut être écrits sur une seule fois.Comme pour la classe overwrite_buffer, lorsqu'une cible reçoit un message d'un objet single_assignment, ce message n'est pas supprimé de cet objet.Par conséquent, plusieurs cibles reçoivent une copie du message.La classe single_assignment est utile lorsque vous voulez diffuser un message à plusieurs composants.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe single_assignment.Cet exemple envoie trois valeurs à un objet single_assignment, puis relit trois fois la valeur actuelle à partir du même objet.Cet exemple est similaire à l'exemple de la classe overwrite_buffer.Bien que les classes overwrite_buffer et single_assignment stockent un seul message, la classe single_assignment est accessible une seule fois en écriture.
// 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 :
33
33
33
Pour obtenir un exemple complet qui indique comment utiliser la classe single_assignment, consultez Procédure pas à pas : implémentation de tâches futures.
Top
Classe call
Le concurrency::call classe agit comme un récepteur de messages qui exécute une fonction de travail lorsqu'il reçoit des données.Cette fonction de travail peut être une expression lambda, un objet de fonction ou un pointeur fonction.Un objet call se comporte différemment d'un appel de fonction ordinaire car il agit en parallèle aux autres composants qui lui envoient des messages.Si un objet call exécute un travail lorsqu'il reçoit un message, il ajoute ce message à une file d'attente.Chaque objet call traite les messages en file d'attente dans l'ordre dans lequel ils sont reçus.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe call.Cet exemple crée un objet call qui imprime chaque valeur qu'il reçoit dans la console.L'exemple envoie ensuite trois valeurs à l'objet call.Étant donné que l'objet call traite les messages dans un thread séparé, cet exemple utilise également une variable de compteur et un objet event pour vérifier que l'objet call traite tous les messages avant que la fonction wmain retourne une valeur.
// 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 :
33
44
55
Pour obtenir un exemple complet qui indique comment utiliser la classe call, consultez Comment : fournir des fonctions de travail aux classes call et transformer.
Top
Classe transformer
Le concurrency::transformer classe agit comme les deux un récepteur de messages et comme un expéditeur du message.La classe transformer ressemble à la classe call, car elle exécute une fonction de travail définie par l'utilisateur lorsqu'elle reçoit des données.Toutefois, la classe transformer envoie également le résultat de la fonction de travail à des objets récepteurs.Comme un objet call, un objet transformer agit en parallèle à d'autres composants qui lui envoient des messages.Si un objet transformer exécute un travail lorsqu'il reçoit un message, il ajoute ce message à une file d'attente.Chaque objet transformer traite ses messages en file d'attente dans l'ordre dans lequel ils sont reçus.
La classe transformer envoie son message à une cible.Si vous définissez la _PTarget paramètre dans le constructeur pour NULL, vous pouvez spécifier ultérieurement la cible en appelant le concurrency::link_target méthode.
Contrairement à tous les autres types de blocs de messages asynchrones fournis par la Bibliothèque d'agents, la classe transformer peut agir sur différents types d'entrées et de sorties.Cette capacité à transformer des données d'un type en un autre fait de la classe transformer un composant clé dans de nombreux réseaux simultanés.De plus, vous pouvez ajouter d'autres fonctionnalités parallèles affinées dans la fonction de travail d'un objet transformer.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe transformer.Cet exemple crée un objet transformer qui multiplie chaque valeur d'entrée int par 0,33 afin de produire une valeur double en sortie.L'exemple accepte ensuite les valeurs transformées du même objet transformer et les imprime sur 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.89
14.52
18.15
Pour obtenir un exemple complet qui indique comment utiliser la classe transformer, consultez Comment : utiliser la classe transformer dans un pipeline de données.
Top
Classe choice
Le concurrency::choice classe sélectionne le premier message disponible à partir d'un ensemble de sources.La classe choice représente un mécanisme de flux de contrôle plutôt qu'un mécanisme de flux de données (la rubrique Bibliothèque d'agents asynchrones décrit les différences entre les flux de données et les flux de contrôle).
La lecture d'un objet choice s'apparente à l'appel de la fonction WaitForMultipleObjects de l'API Windows lorsque le paramètre bWaitAll a la valeur FALSE.Toutefois, la classe choice lie les données à l'événement lui-même plutôt qu'à un objet de synchronisation externe.
En règle générale, vous utilisez le choice de classe avec la concurrency::receive fonction pour piloter les flux de contrôle dans votre application.Utilisez la classe choice lorsque vous devez sélectionner parmi des tampons de messages qui ont des types différents.Utilisez la classe single_assignment lorsque vous devez sélectionner parmi des tampons de messages qui ont le même type.
L'ordre dans lequel vous liez les sources à un objet choice est important car il peut déterminer quel message est sélectionné.Par exemple, considérez le cas dans lequel vous liez plusieurs tampons de messages qui contiennent déjà un message à un objet choice.L'objet choice sélectionne le message à partir de la première source à laquelle il est lié.Une fois que vous avez lié toutes les sources, l'objet choice conserve l'ordre dans lequel chaque source reçoit un message.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe choice.Cet exemple utilise la concurrency::make_choice fonction pour créer un choice objet sélectionne parmi les trois blocs de message.L'exemple calcule ensuite plusieurs nombres de Fibonacci et stocke chaque résultat dans un bloc de message distinct.L'exemple imprime ensuite sur 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 génère l'exemple de sortie suivant :
fib35 received its value first. Result = 9227465
Étant donné qu'il n'est pas garanti que la tâche qui calcule le 35ème nombre de Fibonacci se termine en premier, le résultat de cet exemple peut varier.
Cet exemple utilise la concurrency::parallel_invoke algorithme pour calculer les nombres de Fibonacci en parallèle.Pour plus d'informations sur parallel_invoke, consultez Algorithmes parallèles.
Pour obtenir un exemple complet qui indique comment utiliser la classe choice, consultez Comment : effectuer une sélection parmi les tâches terminées.
Top
Classes join et multitype_join
Le concurrency::join et concurrency::multitype_join classes vous permettent d'attendre que chaque membre d'un ensemble de sources recevoir un message.La classe join agit sur les objets sources qui ont un type de message commun.La classe multitype_join agit sur les objets source qui peuvent avoir des types de messages différents.
La lecture d'un objet join ou multitype_join s'apparente à l'appel de la fonction WaitForMultipleObjects de l'API Windows lorsque le paramètre bWaitAll a la valeur TRUE.Cependant, tout 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 objet join génère un objet std::vector.La lecture à partir d'un objet multitype_join génère un objet std::tuple.Les éléments apparaissent dans ces objets dans le même ordre que celui dans lequel leurs mémoires tampons sources correspondantes sont liées à l'objet join ou multitype_join.Étant donné que l'ordre dans lequel vous associez des mémoires tampons sources à un objet join ou multitype_join dépend de l'ordre des éléments de l'objet vector ou tuple qui en résulte, nous vous conseillons de ne pas dissocier une mémoire tampon source d'une jointure.Cela pourrait entraîner un comportement non spécifié.
Jointures gourmandes et non gourmandes
Les classes multitype_join et join prennent en charge le concept de jointures gourmandes et non gourmandes.Une jointure gourmande accepte un message de chacune de ses sources à mesure que les messages deviennent disponibles jusqu'à ce que tous les messages soient disponibles.Une jointure non gourmande reçoit les messages en deux phases.Tout d'abord, une jointure non gourmande attend jusqu'à la réception d'un message de chacune de ses sources.Ensuite, lorsque tous les messages sources sont disponibles, une jointure non gourmande tente de réserver chacun de ces messages.Si chaque message peut être réservé, elle consomme tous les messages et les propage à sa cible.Dans le cas contraire, elle libère ou elle annule les réservations de messages et attend à nouveau que chaque source reçoive un message.
Les jointures gourmandes sont plus performantes que les jointures non gourmandes car elles acceptent les messages immédiatement. Toutefois, dans de rares cas, les jointures gourmandes peuvent provoquer des interblocages.Utilisez une jointure non gourmande lorsque vous avez plusieurs jointures qui contiennent un ou plusieurs objets sources partagés.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe join.Cet exemple utilise la concurrency::make_join fonction pour créer un join objet qui reçoit de trois single_assignment objets.Cet exemple calcule plusieurs nombres de Fibonacci, stocke chaque résultat dans un objet différent single_assignment, puis imprime sur la console chaque résultat que l'objet join contient.Cet exemple est semblable à l'exemple de la classe choice, hormis le fait que la classe join attend que tous les blocs de messages sources 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 = 9227465
fib37 = 24157817
half_of_fib42 = 1.33957e+008
Cet exemple utilise la concurrency::parallel_invoke algorithme pour calculer les nombres de Fibonacci en parallèle.Pour plus d'informations sur parallel_invoke, consultez Algorithmes parallèles.
Pour obtenir des exemples complets illustrant l'utilisation de la classe join, consultez Comment : effectuer une sélection parmi les tâches terminées et Procédure pas à pas : utilisation de la classe join pour empêcher l'interblocage.
Top
Classe timer
Le concurrency::timer classe agit comme une source de message.Un objet timer envoie un message à une cible après l'expiration d'un certain délai.La classe timer est utile lorsque vous devez différer l'envoi d'un message ou que vous voulez envoyer un message à intervalle régulier.
La classe timer envoie son message à une seule cible.Si vous définissez la _PTarget paramètre dans le constructeur pour NULL, vous pouvez spécifier ultérieurement la cible en appelant le concurrency::ISource::link_target méthode.
Un objet timer peut répétitif ou non répétitif.Pour créer une minuterie répétitive, passez true pour le paramètre _Repeating lorsque vous appelez le constructeur.Sinon, passez false pour le paramètre _Repeating pour créer une minuterie non répétitive.Si la minuterie est répétitive, elle envoie le même message à sa cible après chaque intervalle.
La Bibliothèque d'agents crée les objets timer à l'état non démarré.Pour démarrer un objet timer, appelez le concurrency::timer::start méthode.Pour arrêter une timer d'objet, de détruire l'objet ou l'appel de la concurrency::timer::stop méthode.Pour interrompre un minuteur extensible, appelez le concurrency::timer::pause méthode.
Exemple
L'exemple suivant illustre la structure de base pour savoir utiliser la classe timer.L'exemple utilise des objets timer et call pour indiquer la progression d'une longue opération.
// 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 génère l'exemple de sortie suivant :
Computing fib(42)..................................................
result is 267914296
Pour obtenir un exemple complet qui indique comment utiliser la classe timer, consultez Comment : envoyer un message à intervalles réguliers.
Top
Filtrage de messages
Lorsque vous créez un objet de bloc de message, vous pouvez fournir une fonction de filtre qui détermine si le bloc de message accepte ou rejette un message.Une fonction de filtre est un moyen utile pour garantir qu'un bloc de message ne reçoit que certaines valeurs.
L'exemple suivant indique comment créer un objet unbounded_buffer qui utilise une fonction de filtre pour accepter uniquement des chiffres pairs.L'objet unbounded_buffer rejette les nombres impairs. Par conséquent, il ne propage pas les nombres impairs à ses 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 filtre peut être une fonction lambda, un pointeur fonction ou un objet de fonction.Chaque fonction de filtre se présente sous l'une des formes suivantes.
bool (_Type)
bool (_Type const &)
Pour éviter la copie inutile des données, utilisez la deuxième forme lorsque le type d'agrégat que vous utilisez est propagé par valeur.
Le filtrage de message prend en charge le modèle de programmation du flux de données, dans lequel les composants exécutent des calculs lorsqu'ils reçoivent des données.Pour obtenir des exemples qui utilisent des fonctions de filtre pour contrôler le flux de données dans un réseau de passage de message, consultez Comment : utiliser un filtre de bloc de message, Procédure pas à pas : création des agents de flux de données et Procédure pas à pas : création d'un réseau de traitement d'image.
Top
Réservation de messages
La réservation de messages permet à un bloc de message de réserver un message pour l'utiliser ultérieurement.En général, la réservation de messages n'est pas utilisée directement.Toutefois, comprendre la réservation de messages peut vous aider à mieux comprendre le comportement de certains types de bloc de message prédéfinis.
Prenez en considération les jointures gourmandes et non gourmandes.Les deux types de jointures utilisent la réservation de messages pour réserver des messages pour une utilisation ultérieure.Comme indiqué précédemment, une jointure non gourmande reçoit des messages en deux phases.Lors de la première phase, un objet join de jointure non gourmande attend que chaque source reçoive un message.Une jointure non gourmande tente alors de réserver chacun de ces messages.Si chaque message peut être réservé, elle consomme tous les messages et les propage à sa cible.Dans le cas contraire, elle libère ou elle annule les réservations de messages et attend à nouveau que chaque source reçoive un message.
Une jointure gourmande, qui lit également les messages d'entrée provenant de plusieurs sources, utilise la réservation de messages pour lire les messages supplémentaires tout en attendant de recevoir un message de chaque source.Prenons l'exemple d'une jointure gourmande qui reçoit des messages des blocs de message A et B.Si la jointure gourmande reçoit deux messages de B, mais qu'elle n'a pas encore reçu un message de A, la jointure gourmande enregistre l'identificateur unique de message du deuxième message provenant de B.Lorsque la jointure gourmande reçoit un message de A et qu'elle propage ces messages, elle utilise l'identificateur de message stocké pour voir si le deuxième message de B est toujours disponible.
Vous pouvez utiliser la réservation de messages lorsque vous implémentez vos propres types de bloc de message personnalisés.Pour obtenir un exemple illustrant la création d'un type de bloc de message personnalisé, consultez Procédure pas à pas : création d'un bloc de message personnalisé.
Top