Como usar um filtro de bloco de mensagens

Este documento demonstra como usar uma função de filtro para permitir que um bloco de mensagens assíncrona aceite ou rejeite uma mensagem com base no conteúdo dessa mensagem.

Quando você cria um objeto de bloco de mensagem, como concurrency::unbounded_buffer, concurrency::call:: ou concurrency::transformer, pode fornecer uma função de filtro que determina se o bloco de mensagem aceita ou rejeita uma mensagem. Uma função de filtro é um modo útil de garantir que um bloco de mensagens receba apenas determinados valores.

As funções de filtro são importantes porque permitem que você conecte blocos de mensagens para formar redes de fluxo de dados. Em uma rede de fluxo de dados, os blocos de mensagens controlam o fluxo de dados processando apenas as mensagens que atendem a critérios específicos. Compare isso com o modelo de fluxo de controle, em que o fluxo de dados é regulado usando estruturas de controle, como instruções condicionais, loops etc.

Este documento fornece um exemplo básico de como usar um filtro de mensagem. Para exemplos adicionais que usam filtros de mensagem e o modelo de fluxo de dados para conectar blocos de mensagens, confira Guia passo a passo: Como criar um agente de fluxo de dados e Guia passo a passo: como criar uma rede de processamento de imagem.

Exemplo: função count_primes

Considere a função a seguir, count_primes, que ilustra o uso básico de um bloco de mensagens que não filtra mensagens de entrada. O bloco de mensagem acrescenta números primos a um objeto std::vector. A função count_primes envia vários números para o bloco de mensagens, recebe os valores de saída do bloco de mensagens e imprime os números que são primos para o 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;
    }
}

O objeto transformer processa todos os valores de entrada; no entanto, ele requer apenas os valores que são primos. Embora o aplicativo possa ser gravado para que o remetente de mensagens envie apenas números primos, os requisitos do receptor de mensagem nem sempre podem ser conhecidos.

Exemplo: função count_primes_filter

A função count_primes_filter a seguir executa a mesma tarefa que a função count_primes. No entanto, o objeto transformer nesta versão usa uma função de filtro para aceitar apenas os valores primos. A função que executa a ação recebe apenas números primos; portanto, não precisa chamar a função is_prime.

Como o objeto transformer recebe apenas números primos, o objeto transformer em si pode conter os números primos. Em outras palavras, o objeto transformer neste exemplo não é necessário para adicionar os números primos ao 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;
    }
}

O objeto transformer agora processa apenas os valores que são primos. No exemplo anterior, o objeto transformer processa todas as mensagens. Portanto, o exemplo anterior deve receber o mesmo número de mensagens enviadas. Este exemplo usa o resultado da função concurrency::send para determinar quantas mensagens receber do objeto transformer. A função send retorna true quando o buffer de mensagem aceita a mensagem e false quando o buffer de mensagem rejeita a mensagem. Portanto, o número de vezes que o buffer de mensagem aceita a mensagem corresponde à contagem de números primos.

Exemplo: exemplo de código de filtro de bloco de mensagem concluído

O código a seguir mostra um exemplo completo. O exemplo chama a função count_primes e a função 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
    */
}

Compilando o código

Copie o código de exemplo e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado primes-filter.cpp e execute o comando a seguir em uma janela do Prompt de comando do Visual Studio.

cl.exe /EHsc primes-filter.cpp

Programação robusta

Uma função de filtro pode ser uma função lambda, um ponteiro de função ou um objeto de função. Cada função de filtro usa uma das seguintes formas:

bool (T)
bool (T const &)

Para eliminar a cópia desnecessária de dados, use a segunda forma quando você tiver um tipo de agregação transmitido por valor.

Confira também

Biblioteca de agentes assíncronos
Instruções passo a passo: criando um agente de fluxo de dados
Instruções passo a passo: criando uma rede de processamento de imagem
Classe transformer