Comment : utiliser un filtre de bloc de message
Ce document montre comment utiliser une fonction de filtre pour permettre à un bloc de message asynchrone d'accepter ou de rejeter un message en fonction de sa charge utile.
Lorsque vous créez un bloc de message, tel que concurrency::unbounded_buffer, concurrency::call ou concurrency::transformer, 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.
Les fonctions de filtre sont des fonctions importantes, car elles vous permettent de connecter des blocs de messages pour constituer des réseaux de flux de données. Dans un réseau de flux de données, les blocs de messages contrôlent le flux de données en traitant seulement les messages qui répondent à des critères spécifiques. Vous pouvez comparer ceci au modèle de flux de contrôle, dans lequel le flux de données est régulé à l'aide de structures de contrôle, telles que des instructions conditionnelles, des boucles, etc.
Ce document fournit un exemple de base de l'utilisation d'un filtre de messages. Pour obtenir des exemples supplémentaires qui utilisent les filtres de messages et le modèle de flux de données pour connecter des blocs de messages, consultez 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.
Exemple
Prenez en considération la fonction suivante, count_primes, qui illustre l'utilisation de base d'un bloc de message qui ne filtre pas les messages entrants. Le bloc de message ajoute des nombres premiers à un objet std::vector. La fonction count_primes envoie plusieurs numéros au bloc de message, reçoit les valeurs de sortie à partir du bloc de message et imprime ces nombres premiers sur la console.
// Illustrates usage of a message buffer that does not use filtering.
void count_primes(unsigned long random_seed)
{
// Holds prime numbers.
vector<unsigned long> primes;
// Adds numbers that are prime to the vector object.
transformer<unsigned long, unsigned long> t([&primes](unsigned long n) -> unsigned long
{
if (is_prime(n))
{
primes.push_back(n);
}
return n;
});
// Send random values to the message buffer.
mt19937 generator(random_seed);
for (int i = 0; i < 20; ++i)
{
send(t, static_cast<unsigned long>(generator()%10000));
}
// Receive from the message buffer the same number of times
// to ensure that the message buffer has processed each message.
for (int i = 0; i < 20; ++i)
{
receive(t);
}
// Print the prime numbers to the console.
wcout << L"The following numbers are prime: " << endl;
for(unsigned long prime : primes)
{
wcout << prime << endl;
}
}
L'objet transformer traite toutes les valeurs d'entrée. Il requiert cependant uniquement les valeurs premières. Bien que l'application puisse être écrite de manière à ce que l'expéditeur du message envoie uniquement des nombres premiers, les exigences du récepteur de messages ne peuvent pas toujours être connues.
La fonction suivante, count_primes_filter effectue la même tâche que la fonction count_primes. Toutefois, l'objet transformer de cette version utilise une fonction de filtre pour accepter uniquement les valeurs premières. La fonction qui exécute l'action ne reçoit que des nombres premiers. Par conséquent, elle ne doit pas appeler la fonction is_prime.
Étant donné que l'objet transformer ne reçoit que des nombres premiers, l'objet transformer lui-même peut contenir des nombres premiers. En d'autres termes, l'objet transformer de cet exemple n'est pas obligatoire pour ajouter les nombres premiers à l'objet vector.
// Illustrates usage of a message buffer that uses filtering.
void count_primes_filter(unsigned long random_seed)
{
// Accepts numbers that are prime.
transformer<unsigned long, unsigned long> t([](unsigned long n) -> unsigned long
{
// The filter function guarantees that the input value is prime.
// Return the input value.
return n;
},
nullptr,
[](unsigned long n) -> bool
{
// Filter only values that are prime.
return is_prime(n);
});
// Send random values to the message buffer.
mt19937 generator(random_seed);
size_t prime_count = 0;
for (int i = 0; i < 20; ++i)
{
if (send(t, static_cast<unsigned long>(generator()%10000)))
{
++prime_count;
}
}
// Print the prime numbers to the console.
wcout << L"The following numbers are prime: " << endl;
while (prime_count-- > 0)
{
wcout << receive(t) << endl;
}
}
L'objet transformer traite désormais uniquement les valeurs premières. Dans l'exemple précédent, l'objet transformer traite tous les messages. Par conséquent, l'exemple précédent doit recevoir le même nombre de messages que le nombre de messages qu'il envoie. Cet exemple utilise la fonction concurrency::send pour déterminer le nombre de messages à recevoir de l'objet transformer. La fonction send retourne la valeur true lorsque le tampon de messages accepte le message et la valeur false lorsque le tampon de messages rejette le message. Par conséquent, le nombre de fois que le tampon de messages accepte le message correspond au nombre de nombres premiers.
Le code suivant illustre l'exemple complet. L'exemple appelle la fonction count_primes et la fonction count_primes_filter.
// primes-filter.cpp
// compile with: /EHsc
#include <agents.h>
#include <algorithm>
#include <iostream>
#include <random>
using namespace concurrency;
using namespace std;
// Determines whether the input value is prime.
bool is_prime(unsigned long n)
{
if (n < 2)
return false;
for (unsigned long i = 2; i < n; ++i)
{
if ((n % i) == 0)
return false;
}
return true;
}
// Illustrates usage of a message buffer that does not use filtering.
void count_primes(unsigned long random_seed)
{
// Holds prime numbers.
vector<unsigned long> primes;
// Adds numbers that are prime to the vector object.
transformer<unsigned long, unsigned long> t([&primes](unsigned long n) -> unsigned long
{
if (is_prime(n))
{
primes.push_back(n);
}
return n;
});
// Send random values to the message buffer.
mt19937 generator(random_seed);
for (int i = 0; i < 20; ++i)
{
send(t, static_cast<unsigned long>(generator()%10000));
}
// Receive from the message buffer the same number of times
// to ensure that the message buffer has processed each message.
for (int i = 0; i < 20; ++i)
{
receive(t);
}
// Print the prime numbers to the console.
wcout << L"The following numbers are prime: " << endl;
for(unsigned long prime : primes)
{
wcout << prime << endl;
}
}
// Illustrates usage of a message buffer that uses filtering.
void count_primes_filter(unsigned long random_seed)
{
// Accepts numbers that are prime.
transformer<unsigned long, unsigned long> t([](unsigned long n) -> unsigned long
{
// The filter function guarantees that the input value is prime.
// Return the input value.
return n;
},
nullptr,
[](unsigned long n) -> bool
{
// Filter only values that are prime.
return is_prime(n);
});
// Send random values to the message buffer.
mt19937 generator(random_seed);
size_t prime_count = 0;
for (int i = 0; i < 20; ++i)
{
if (send(t, static_cast<unsigned long>(generator()%10000)))
{
++prime_count;
}
}
// Print the prime numbers to the console.
wcout << L"The following numbers are prime: " << endl;
while (prime_count-- > 0)
{
wcout << receive(t) << endl;
}
}
int wmain()
{
const unsigned long random_seed = 99714;
wcout << L"Without filtering:" << endl;
count_primes(random_seed);
wcout << L"With filtering:" << endl;
count_primes_filter(random_seed);
/* Output:
9973
9349
9241
8893
1297
7127
8647
3229
With filtering:
The following numbers are prime:
9973
9349
9241
8893
1297
7127
8647
3229
*/
}
Compilation du code
Copiez l'exemple de code et collez-le dans un projet Visual Studio ou dans un fichier nommé primes-filter.cpp, puis exécutez la commande suivante dans une fenêtre d'invite de commandes Visual Studio.
cl.exe /EHsc primes-filter.cpp
Programmation fiable
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 :
Pour éviter la copie inutile des données, utilisez la deuxième forme lorsque le type d'agrégat que vous utilisez est transmis par valeur.
Voir aussi
Tâches
Procédure pas à pas : création des agents de flux de données
Procédure pas à pas : création d'un réseau de traitement d'image