Cómo: Utilizar un filtro de bloque de mensaje

En este documento se muestra cómo usar una función de filtro para permitir que un bloque de mensajes asincrónicos acepte o rechace un mensaje basándose en la carga de ese mensaje.

Cuando se crea un objeto de bloque de mensajes como concurrency::unbounded_buffer, concurrency::call o concurrency::transformer, se puede proporcionar una función de filtro que determina si el bloque de mensajes acepta o rechaza un mensaje. Una función de filtro resulta útil para garantizar que un bloque de mensajes recibe solo ciertos valores.

Las funciones de filtro son importantes porque permiten conectar los bloques de mensajes para formar redes de flujo de datos. En una red de flujo de datos, los bloques de mensajes controlan el flujo de datos y procesan solo los mensajes que cumplen determinados criterios. Compare esto con el modelo de flujo de control, donde el flujo de datos se regula con las estructuras de control, como instrucciones condicionales, bucles, etc.

En este documento se proporciona un ejemplo básico de cómo usar un filtro de mensajes. Para obtener ejemplos adicionales que usan filtros de mensajes y el modelo de flujo de datos para conectar bloques de mensajes, consulta Tutorial: Creación de un agente de flujo de datos y Tutorial: Creación de una red de Image-Processing.

Ejemplo: count_primes function

Considere la siguiente función, count_primes, que muestra el uso básico de un bloque de mensajes que no filtra los mensajes entrantes. El bloque de mensajes anexa los números primos en un objeto std::vector. La función count_primes envía varios números al bloque de mensajes, recibe los valores de salida del bloque de mensajes e imprime aquellos números que son primos en la consola.

// 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;
    }
}

El objeto transformer procesa todos los valores de entrada; sin embargo, solo requiere los valores que son primos. Aunque la aplicación podría escribirse de forma que el remitente del mensaje envíe solo números primos, los requisitos del receptor del mensaje no se pueden conocer siempre.

Ejemplo: count_primes_filter function

La siguiente función, count_primes_filter, realiza la misma tarea que la función count_primes. Sin embargo, el objeto transformer de esta versión usa una función de filtro para aceptar únicamente los valores que son primos. La función que realiza la acción recibe solo números primos; por consiguiente, no tiene que llamar a la función is_prime.

Dado que el objeto transformer recibe solo números primos, el propio objeto transformer puede contener números primos. Es decir, el objeto transformer de este ejemplo no es necesario para agregar los números primos al objeto 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;
    }
}

El objeto transformer procesa ahora solo los valores que son primos. En el ejemplo anterior, el objeto transformer procesa todos los mensajes. Por consiguiente, en el ejemplo anterior debe recibir el mismo número de mensajes que envía. En este ejemplo se usa el resultado de la función concurrency::send para determinar cuántos mensajes se reciben del objeto transformer. La función send devuelve true cuando el búfer de mensajes acepta el mensaje y false cuando el búfer de mensajes rechaza el mensaje. Por tanto, el número de veces que el búfer de mensajes acepta el mensaje coincide con el contador de números primos.

Ejemplo: Ejemplo de código de filtro de bloque de mensajes finalizado

En el código siguiente se muestra el ejemplo completo. En el ejemplo se llama a las funciones count_primes y 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
    */
}

Compilar el código

Copie el código de ejemplo y péguelo en un proyecto de Visual Studio o en un archivo denominado primes-filter.cpp y, después, ejecute el siguiente comando en una ventana del símbolo del sistema de Visual Studio.

cl.exe /EHsc primes-filter.cpp

Programación sólida

Una función de filtro puede ser una función lambda, un puntero a función o un objeto de función. Cada función de filtro toma una de las siguientes formas:

bool (T)
bool (T const &)

Para eliminar la copia innecesaria de datos, use el segundo formulario cuando tenga un tipo agregado que se transmite por valor.

Consulte también

Biblioteca de agentes asincrónicos
Tutorial: Crear un agente de flujo de datos
Tutorial: Crear una red de procesamiento de imagen
transformer (clase)