Procedura: utilizzare il filtro di blocco dei messaggi

In questo documento viene illustrato come utilizzare una funzione di filtro per consentire a un blocco di messaggi asincroni di accettare o rifiutare un messaggio in base al payload del messaggio.

Quando si crea un oggetto blocco di messaggi, ad esempio Concurrency::unbounded_buffer, Concurrency::call oppure Concurrency::transformer, è possibile fornire una funzione di filtro in base alla quale si stabilisce se il blocco di messaggi deve accettare o rifiutare un messaggio. Le funzioni di filtro garantiscono che un blocco di messaggi riceva solo determinati valori.

Le funzioni di filtro sono importanti in quanto consentono di connettere blocchi di messaggi per formare reti di flussi di dati. In una rete di flussi di dati i blocchi di messaggi controllano il flusso di dati mediante l'elaborazione solo dei messaggi che soddisfano determinati criteri. Confrontare questo modello con quello del flusso di controllo, in cui il flusso di dati viene modificato mediante strutture di controllo come istruzioni condizionali, cicli e così via.

In questo documento viene fornito un esempio di base dell'utilizzo di un filtro messaggi. Per ulteriori esempi che utilizzano i filtri dei messaggi e il modello di flusso di dati per connettere blocchi di messaggi, vedere Procedura dettagliata: creazione di un agente del flusso di dati e Procedura dettagliata: creazione di una rete per l'elaborazione di immagini.

Esempio

Si consideri la funzione seguente, count_primes, che illustra l'utilizzo di base di un blocco di messaggi che non filtra i messaggi in arrivo. l blocco di messaggi accoda i numeri primi a un oggetto std::vector. La funzione count_primes invia diversi numeri al blocco di messaggi, riceve i valori di output dal blocco di messaggi e stampa nella console i numeri primi.

// 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, 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_each(primes.begin(), primes.end(), [](unsigned long prime) {
      wcout << prime << endl;
   });
}

L'oggetto transformer elabora tutti i valori di input ma richiede solo i valori primi. L'applicazione potrebbe essere scritta in modo che il mittente del messaggio invii solo i numeri primi, tuttavia non accade sempre che i requisiti del ricevitore del messaggio siano noti.

La funzione seguente, count_primes_filter, esegue la stessa attività della funzione count_primes. Tuttavia, l'oggetto transformer in questa versione utilizza una funzione di filtro per accettare solo i valori primi. La funzione che esegue l'azione riceve solo numeri primi, pertanto non è necessario che chiami la funzione is_prime.

Poiché l'oggetto transformer riceve solo numeri primi, può contenere numeri primi. In altri termini, non è necessario che l'oggetto transformer in questo esempio aggiunga i numeri primi all'oggetto 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;
      },
      NULL,      
      [](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, 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'oggetto transformer ora elabora solo i valori primi. Nell'esempio precedente l'oggetto transformer elabora tutti i messaggi. Pertanto l'esempio precedente deve ricevere lo stesso numero di messaggi che invia. In questo esempio viene utilizzato il risultato della funzione Concurrency::send per stabilire il numero di messaggi che deve essere ricevuto dall'oggetto transformer. La funzione send restituisce true se il buffer dei messaggi accetta il messaggio, false se lo rifiuta. Di conseguenza, il numero di volte che il buffer dei messaggi accetta il messaggio corrisponde al conteggio dei numeri primi.

Nel codice seguente viene illustrato l'esempio completo. L'esempio chiama sia la funzione count_primes sia la funzione 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, 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_each(primes.begin(), primes.end(), [](unsigned long prime) {
      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;
      },
      NULL,      
      [](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, 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);
}

Questo esempio produce l'output seguente:

Without filtering:
The following numbers are prime:
9973
9349
9241
8893
1297
7127
8647
3229
With filtering:
The following numbers are prime:
9973
9349
9241
8893
1297
7127
8647
3229

Compilazione del codice

Copiare il codice di esempio e incollarlo in un progetto Visual Studio oppure incollarlo in un file denominato primes-filter.cpp, quindi eseguire il comando seguente in una finestra del prompt dei comandi di Visual Studio 2010.

cl.exe /EHsc primes-filter.cpp

Programmazione efficiente

Una funzione di filtro può essere una funzione lambda, un puntatore a funzione o un oggetto funzione. Ogni funzione di filtro assume uno dei formati indicati di seguito.

bool (_Type)
bool (_Type const &)

Per eliminare la copia non necessaria dei dati, utilizzare il secondo formato quando è presente un tipo di aggregato che viene trasmesso in base al valore.

Vedere anche

Riferimenti

Classe transformer

Concetti

Libreria di agenti asincroni

Altre risorse

Procedura dettagliata: creazione di un agente del flusso di dati

Procedura dettagliata: creazione di una rete per l'elaborazione di immagini