Blocs de messages asynchrones
La bibliothèque d’agents fournit plusieurs types de blocs de messages qui vous permettent de propager des messages entre les composants d’application de manière thread-safe. Ces types de bloc de messages sont souvent utilisés avec les différentes routines de transmission 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 d’agents, consultez Fonctions de passage de messages.
Sections
Cette rubrique contient les sections suivantes :
Sources et cibles
Les sources et les cibles sont deux participants importants à la transmission de messages. 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. Vous pouvez considérer une source comme un point de terminaison à partir duquel vous lisez et une cible comme point de terminaison dans lequel vous écrivez. Les applications connectent des sources et des cibles ensemble pour former des réseaux de messagerie.
La bibliothèque d’agents utilise deux classes abstraites pour représenter des sources et des cibles : concurrency ::ISource et concurrency ::ITarget. Types de blocs de message qui agissent en tant que sources dérivent ISource
; les types de blocs de messages qui agissent en tant que cibles dérivent de ITarget
. Les types de blocs de messages qui agissent en tant que sources et cibles dérivent des deux ISource
et ITarget
.
[Haut]
Propagation de messages
La propagation de messages est l’acte d’envoi d’un message d’un composant à un autre. Lorsqu’un bloc de message est proposé à un message, il peut accepter, refuser ou reporter ce message. Chaque type de bloc de messages stocke et transmet les messages de différentes manières. Par exemple, la unbounded_buffer
classe stocke un nombre illimité de messages, la overwrite_buffer
classe stocke un seul message à la fois et la classe transformateur 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 un travail et, si le bloc de message est une source, transmettez le message résultant à un autre membre du réseau. Un bloc de messages 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 plus loin dans cette rubrique, dans la section Filtrage des 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 plus loin 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 manière asynchrone ou synchrone. Lorsque vous transmettez un message à un bloc de message de manière synchrone, par exemple en utilisant la send
fonction, 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 message de manière asynchrone, par exemple en utilisant la asend
fonction, le runtime offre le message à la cible et, si la cible accepte le message, le runtime planifie une tâche asynchrone qui propage le message au destinataire. 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, consultez Planificateur de tâches.
Les applications connectent des sources et des cibles ensemble pour former des réseaux de messagerie. En règle générale, vous liez le réseau et appelez send
ou asend
transmettez des données au réseau. Pour connecter un bloc de message 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 quitte l’étendue ou est détruit, il se déconnecte automatiquement des 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 types de blocs de messages importants.
Unbounded_buffer
Stocke une file d’attente de messages.
overwrite_buffer
Stocke un message qui peut être écrit dans et lu plusieurs fois.
single_assignment
Stocke un message qui peut être écrit à une seule fois et lu à partir de plusieurs fois.
call
Effectue un travail lorsqu’il reçoit un message.
Transformateur
Effectue un travail lorsqu’il reçoit des données et envoie le résultat de ce travail à un autre bloc cible. La transformer
classe peut agir sur différents types d’entrée et de sortie.
choice
Sélectionne le premier message disponible à partir d’un ensemble de sources.
jointure et jointure multitype
Attendez que tous les messages soient reçus à partir d’un ensemble de sources, puis combinez les messages en un message pour un autre bloc de messages.
Minuterie
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 quelques-unes des caractéristiques suivantes :
Type de propagation : indique si le bloc de messages agit comme une source de données, un récepteur de données ou les deux.
Classement des messages : indique si le bloc de messages conserve l’ordre d’origine dans lequel les messages sont envoyés ou reçus. Chaque type de bloc de messages 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 de laquelle le bloc de messages peut lire.
Nombre de cibles : nombre maximal de cibles dans lesquelles le bloc de messages peut écrire.
Le tableau suivant montre comment ces caractéristiques sont liées aux différents types de blocs de messages.
Type de bloc de message | 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 | Ordered (Validée) | Non lié | Non lié |
overwrite_buffer |
Les deux | Ordered (Validée) | Non lié | Non lié |
single_assignment |
Les deux | Ordered (Validée) | Non lié | Non lié |
call |
Cible | Ordered (Validée) | Non lié | Non applicable |
transformer |
Les deux | Ordered (Validée) | Non lié | 1 |
choice |
Les deux | Ordered (Validée) | 10 | 1 |
join |
Les deux | Ordered (Validée) | Non lié | 1 |
multitype_join |
Les deux | Ordered (Validée) | 10 | 1 |
timer |
Source | Non applicable | Non applicable | 1 |
Les sections suivantes décrivent plus en détail les types de blocs de messages.
[Haut]
Classe unbounded_buffer
La classe concurrency ::unbounded_buffer représente une structure de messagerie asynchrone à usage général. 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’une cible reçoit un message d’un unbounded_buffer
objet, ce message est supprimé de la file d’attente de messages. Par conséquent, bien qu’un unbounded_buffer
objet puisse avoir plusieurs cibles, une seule 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 de l’utilisation de la unbounded_buffer
classe. Cet exemple envoie trois valeurs à un unbounded_buffer
objet, 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 obtenir un exemple complet montrant comment utiliser la unbounded_buffer
classe, consultez How to : Implement Various Producer-Consumer Patterns.
[Haut]
Classe overwrite_buffer
La classe concurrency ::overwrite_buffer ressemble à la unbounded_buffer
classe, sauf qu’un overwrite_buffer
objet stocke un seul message. En outre, lorsqu’une cible reçoit un message d’un overwrite_buffer
objet, ce message n’est pas supprimé de la mémoire tampon. Par conséquent, plusieurs cibles reçoivent une copie du message.
La overwrite_buffer
classe est utile lorsque vous souhaitez transmettre plusieurs messages à un autre composant, mais ce composant n’a besoin que 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 de l’utilisation de la overwrite_buffer
classe. Cet exemple envoie trois valeurs à un overwrite _buffer
objet, puis lit la valeur actuelle à partir du même objet trois fois. Cet exemple est similaire à l’exemple de la unbounded_buffer
classe. Toutefois, la overwrite_buffer
classe stocke un seul message. En outre, le runtime ne supprime pas le message d’un overwrite_buffer
objet 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 obtenir un exemple complet montrant comment utiliser la overwrite_buffer
classe, consultez How to : Implement Various Producer-Consumer Patterns.
[Haut]
Classe single_assignment
La classe concurrency ::single_assignment ressemble à la overwrite_buffer
classe, sauf qu’un single_assignment
objet 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 single_assignment
classe est utile lorsque vous souhaitez diffuser un message vers plusieurs composants.
Exemple
L’exemple suivant montre la structure de base de l’utilisation de la single_assignment
classe. Cet exemple envoie trois valeurs à un single_assignment
objet, puis lit la valeur actuelle à partir du même objet trois fois. Cet exemple est similaire à l’exemple de la overwrite_buffer
classe. Bien que les overwrite_buffer
deux classes single_assignment
stockent un seul message, la single_assignment
classe 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 obtenir un exemple complet montrant comment utiliser la single_assignment
classe, consultez Procédure pas à pas : Implémentation d’futures.
[Haut]
call, classe
La classe concurrency ::call agit en tant que récepteur de messages qui effectue une fonction de travail lorsqu’elle reçoit des données. Cette fonction de travail peut être une expression lambda, un objet de fonction ou un pointeur de fonction. Un call
objet 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 effectue un call
travail lorsqu’il reçoit un message, il ajoute ce message à une file d’attente. Chaque objet traite les call
messages mis en file d’attente dans l’ordre dans lequel ils sont reçus.
Exemple
L’exemple suivant montre la structure de base de l’utilisation de la call
classe. Cet exemple crée un call
objet qui imprime chaque valeur qu’elle reçoit dans la console. L’exemple envoie ensuite trois valeurs à l’objet call
. Étant donné que l’objet call
traite les messages sur un thread distinct, cet exemple utilise également une variable de compteur et un objet d’événement pour s’assurer que l’objet call
traite tous les messages avant que la wmain
fonction 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 obtenir un exemple complet montrant comment utiliser la call
classe, consultez Comment : fournir des fonctions de travail aux classes d’appel et de transformateur.
[Haut]
Classe transformer
La classe concurrency ::transformer agit à la fois comme récepteur de message et en tant qu’expéditeur de message. La transformer
classe ressemble à la call
classe, car elle effectue une fonction de travail définie par l’utilisateur lorsqu’elle reçoit des données. Toutefois, la transformer
classe envoie également le résultat de la fonction de travail aux objets récepteurs. Comme un call
objet, un transformer
objet agit en parallèle avec d’autres composants qui lui envoient des messages. Si un objet effectue un transformer
travail lorsqu’il reçoit un message, il ajoute ce message à une file d’attente. Chaque transformer
objet traite ses messages mis en file d’attente dans l’ordre dans lequel ils sont reçus.
La transformer
classe envoie son message à une cible. Si vous définissez le _PTarget
paramètre dans le constructeur NULL
sur , vous pouvez ultérieurement 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 d’agents, la transformer
classe peut agir sur différents types d’entrée et de sortie. Cette possibilité de transformer des données d’un type à un autre rend la transformer
classe un composant clé dans de nombreux réseaux simultanés. En outre, vous pouvez ajouter des fonctionnalités parallèles affinées dans la fonction de travail d’un transformer
objet.
Exemple
L’exemple suivant montre la structure de base de l’utilisation de la transformer
classe. Cet exemple crée un transformer
objet qui multiple chaque valeur d’entrée int
de 0,33 afin de produire une double
valeur en tant que sortie. L’exemple reçoit ensuite les valeurs transformées du même transformer
objet et les imprime 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 obtenir un exemple complet montrant comment utiliser la transformer
classe, consultez Guide pratique pour utiliser un transformateur dans un pipeline de données.
[Haut]
Classe choice
La classe concurrency ::choice sélectionne le premier message disponible à partir d’un ensemble de sources. La choice
classe représente un mécanisme de flux de contrôle au lieu d’un mécanisme de flux de données (la rubrique Bibliothèque d’agents asynchrones décrit les différences entre le flux de données et le flux de contrôle).
La lecture à partir d’un objet de choix ressemble à appeler la fonction WaitForMultipleObjects
API Windows lorsqu’elle a le bWaitAll
paramètre défini FALSE
sur . Toutefois, la choice
classe lie des données à l’événement lui-même au lieu d’un objet de synchronisation externe.
En règle générale, vous utilisez la choice
classe avec la fonction concurrency ::receive pour piloter le flux de contrôle dans votre application. Utilisez la choice
classe lorsque vous devez sélectionner parmi les mémoires tampons de message qui ont différents types. Utilisez la single_assignment
classe lorsque vous devez sélectionner parmi les mémoires tampons de message qui ont le même type.
L’ordre dans lequel vous liez des sources à un choice
objet est important, car il peut déterminer quel message est sélectionné. Par exemple, considérez le cas où vous liez plusieurs mémoires tampons de message qui contiennent déjà un message à un choice
objet. 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 montre la structure de base de l’utilisation de la choice
classe. Cet exemple utilise la fonction concurrency ::make_choice pour créer un choice
objet qui se sélectionne parmi trois blocs de messages. L’exemple calcule ensuite différents nombres Fibonacci et stocke chaque résultat dans un bloc de message différent. L’exemple imprime ensuite dans la console un message basé sur l’opération 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
Étant donné que la tâche qui calcule le 35ème nombre Fibonacci n’est pas garantie de terminer en premier, la sortie de cet exemple peut varier.
Cet exemple utilise l’algorithme concurrency ::p arallel_invoke pour calculer les nombres Fibonacci en parallèle. Pour plus d’informations sur parallel_invoke
, consultez Algorithmes parallèles.
Pour obtenir un exemple complet montrant comment utiliser la choice
classe, consultez Guide pratique pour sélectionner parmi les tâches terminées.
[Haut]
join and multitype_join Classes
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 join
classe agit sur les objets sources qui ont un type de message commun. La multitype_join
classe agit sur les objets sources qui peuvent avoir différents types de messages.
La lecture à partir d’un ou multitype_join
d’un join
objet ressemble à appeler la fonction WaitForMultipleObjects
API Windows lorsqu’elle a le bWaitAll
paramètre défini TRUE
sur . Toutefois, tout comme un choice
objet, join
et multitype_join
les objets utilisent un mécanisme d’événement qui lie des données à l’événement lui-même au lieu d’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 leurs mémoires tampons sources correspondantes sont liés à l’objet ou multitype_join
à l’objetjoin
. Étant donné que l’ordre dans lequel vous liez des mémoires tampons sources à un ou multitype_join
un join
objet est associé à l’ordre des éléments dans le résultat vector
ou tuple
l’objet, nous vous recommandons de ne pas dissocier une mémoire tampon source existante à partir d’une jointure. Cela peut entraîner un comportement non spécifié.
Jointures gourmandes et non gourmandes
Les join
classes et multitype_join
les classes 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 des messages en deux phases. Tout d’abord, une jointure non gourmande attend qu’elle soit proposée à un message de chacune de ses sources. Ensuite, une fois que tous les messages sources sont disponibles, une jointure non gourmande tente de réserver chacun de ces messages. S’il peut réserver chaque message, il consomme tous les messages et les propage à sa cible. Sinon, il libère ou annule les réservations de messages et attend de nouveau que chaque source reçoive un message.
Les jointures gourmandes fonctionnent mieux que les jointures non gourmandes, car elles acceptent immédiatement les messages. Toutefois, dans de rares cas, des jointures gourmandes peuvent entraîner 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 montre la structure de base de l’utilisation de la join
classe. Cet exemple utilise la fonction concurrency ::make_join pour créer un join
objet qui reçoit de trois single_assignment
objets. Cet exemple calcule différents nombres Fibonacci, stocke chaque résultat dans un objet différent single_assignment
, puis imprime dans la console chaque résultat que l’objet join
contient. Cet exemple est similaire à l’exemple de la choice
classe, sauf que la join
classe attend que tous les blocs de messages sources reçoivent 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 ::p arallel_invoke pour calculer les nombres Fibonacci en parallèle. Pour plus d’informations sur parallel_invoke
, consultez Algorithmes parallèles.
Pour obtenir des exemples complets qui montrent comment utiliser la join
classe, consultez Comment : sélectionner parmi les tâches terminées et procédure pas à pas : utilisation de la jointure pour empêcher l’interblocage.
[Haut]
Classe timer
La classe concurrency ::timer agit en tant que source de message. Un timer
objet envoie un message à une cible après une période spécifiée. La timer
classe est utile lorsque vous devez retarder l’envoi d’un message ou si vous souhaitez envoyer un message à intervalle régulier.
La timer
classe envoie son message à une seule cible. Si vous définissez le _PTarget
paramètre dans le constructeur NULL
sur , vous pouvez ultérieurement spécifier la cible en appelant la méthode concurrency ::ISource ::link_target .
Un timer
objet peut être répétitif ou non répétitif. Pour créer un minuteur répétitif, passez true
le _Repeating
paramètre lorsque vous appelez le constructeur. Sinon, passez false
le _Repeating
paramètre pour créer un minuteur non répétitif. Si le minuteur se répète, il envoie le même message à sa cible après chaque intervalle.
La bibliothèque agents crée des timer
objets dans l’état non démarré. Pour démarrer un objet minuteur, appelez la méthode concurrency ::timer ::start . Pour arrêter un timer
objet, détruisez l’objet ou appelez la méthode concurrency ::timer ::stop . Pour suspendre un minuteur répétitif, appelez la méthode concurrency ::timer ::p ause .
Exemple
L’exemple suivant montre la structure de base de l’utilisation de la timer
classe. L’exemple utilise et call
des timer
objets pour signaler 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 produit l’exemple de sortie suivant :
Computing fib(42)..................................................result is 267914296
Pour obtenir un exemple complet montrant comment utiliser la timer
classe, consultez How to : Send a Message at a Regular Interval.
[Haut]
Filtrage des messages
Lorsque vous créez un objet de bloc de messages, vous pouvez fournir une fonction de filtre qui détermine si le bloc de messages accepte ou rejette un message. Une fonction de filtre est un moyen utile de garantir qu’un bloc de messages ne reçoit que certaines valeurs.
L’exemple suivant montre comment créer un unbounded_buffer
objet qui utilise une fonction de filtre pour accepter uniquement des nombres pairs. L’objet unbounded_buffer
rejette les nombres impairs et ne propage donc 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 de fonction ou un objet de fonction. Chaque fonction de filtre prend l’une des formes suivantes.
bool (T)
bool (T const &)
Pour éliminer la copie inutile de données, utilisez le deuxième formulaire lorsque vous disposez d’un type d’agrégation propagé par valeur.
Le filtrage des messages prend en charge le modèle de programmation de flux de données, dans lequel les composants effectuent 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 d’un réseau de transmission de messages, consultez Comment : utiliser un filtre de bloc de messages, procédure pas à pas : création d’un agent de flux de données et procédure pas à pas : création d’un réseau de traitement d’images.
[Haut]
Réservation de messages
La réservation de messages permet à un bloc de message de réserver un message pour une utilisation ultérieure. En règle générale, la réservation de messages n’est pas utilisée directement. Toutefois, la compréhension de la réservation de messages peut vous aider à mieux comprendre le comportement de certains types de blocs de messages prédéfinis.
Considérez les jointures gourmandes et gourmandes. Ces deux types utilisent la réservation de messages pour réserver des messages pour une utilisation ultérieure. Une jointure non gourmande, décrite précédemment, reçoit des messages en deux phases. Pendant la première phase, un objet non gourmand join
attend que chacune de ses sources reçoive un message. Une jointure non gourmande tente ensuite de réserver chacun de ces messages. S’il peut réserver chaque message, il consomme tous les messages et les propage à sa cible. Sinon, il libère ou annule les réservations de messages et attend de nouveau que chaque source reçoive un message.
Une jointure gourmande, qui lit également les messages d’entrée à partir d’un certain nombre de sources, utilise la réservation de messages pour lire des messages supplémentaires pendant qu’il attend de recevoir un message de chaque source. Par exemple, considérez une jointure gourmande qui reçoit les messages des blocs A
de messages et B
. Si la jointure gourmande reçoit deux messages de B mais n’a pas encore reçu de message, A
la jointure gourmande enregistre l’identificateur de message unique pour le deuxième message de B
. Une fois que la jointure gourmande reçoit un message et A
propage ces messages, il utilise l’identificateur de message enregistré pour voir si le deuxième message est B
toujours disponible.
Vous pouvez utiliser la réservation de messages lorsque vous implémentez vos propres types de blocs de messages personnalisés. Pour obtenir un exemple sur la création d’un type de bloc de message personnalisé, consultez Procédure pas à pas : Création d’un bloc de messages personnalisé.
[Haut]