Практическое руководство. Использование фильтра блока сообщений

В этом документе показано, как использовать функцию фильтрации, чтобы позволить асинхронному блоку сообщений принимать или отклонять сообщения в зависимости от их полезной нагрузки.

При создании такого объекта блока сообщений, как Concurrency::unbounded_buffer, Concurrency::call или Concurrency::transformer, можно предоставить функцию фильтрации, определяющую для блока сообщений, принимает он или отклоняет сообщение. Функция фильтрации позволяет блоку сообщений получать только определенные значения.

Функции фильтрации важны, так как они позволяют соединять блоки сообщений в сети потока данных. В сети потока данных блоки сообщений управляют потоком данных, обрабатывая только сообщения, удовлетворяющие определенным критериям. Это отличается от модели потока управления, где поток данных контролируется с помощью структур управления, например условных операторов, циклов и так далее.

В этом документе содержится основной пример использования фильтра сообщений. Дополнительные примеры использования фильтров сообщений и модели потока данных для соединения блоков сообщений см. в разделах Пошаговое руководство. Создание агента потоков данных и Пошаговое руководство. Создание сети обработки изображений.

Пример

Рассмотрим следующую функцию, count_primes, показывающую основное использование блока сообщений, не фильтрующего входящие сообщения. Блок сообщений добавляет простые числа к объекту std::vector. Функция count_primes отправляет несколько чисел блоку сообщений, принимает выходные значения от блока сообщений и выводит простые числа на консоль.

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

Объект transformer обрабатывает все входные значения; при этом требуются только простые числа. Можно было написать приложение так, чтобы отправитель сообщений отправлял только простые числа, но требования получателя сообщений не всегда известны.

Следующая функция, count_primes_filter, выполняет ту же задачу, что и функция count_primes. При этом объект transformer в этой версии использует функцию фильтрации, чтобы принимать только простые числа. Функция, выполняющая действие, принимает только простые числа; поэтому ей не нужно вызывать функцию is_prime.

Так как объект transformer принимает только простые числа, объект transformer может сам содержать простые числа. Иными словами, объект transformer в этом примере не должен добавлять простые числа к объекту 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;
}

После этого объект transformer обрабатывает только простые числа. В предыдущем примере объект transformer обрабатывает все сообщения. Поэтому в нем количества получаемых и отправляемых сообщений должны совпадать. В этом примере результат функции Concurrency::send используется, чтобы определить, сколько сообщений нужно получить от объекта transformer. Функция send возвращает значение true, если буфер сообщений принимает значение, и значение false, если буфер сообщений отклоняет сообщение. Поэтому количество простых чисел совпадает с количеством сообщений, принятых буфером сообщений.

Ниже приведен полный пример кода. Пример вызывает и функцию count_primes, и функцию 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);
}

После выполнения примера получается следующий результат.

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

Компиляция кода

Скопируйте код примера и вставьте его в проект Visual Studio или в файл с именем primes-filter.cpp, затем выполните в окне командной строки Visual Studio 2010 следующую команду.

cl.exe /EHsc primes-filter.cpp

Отказоустойчивость

Функция фильтрации может быть лямбда-функцией, указателем функции или объектом функции. Любая функция фильтрации принимает одну из следующих форм.

bool (_Type)
bool (_Type const &)

Чтобы исключить лишнее копирование данных, при работе с составным типом, передаваемым по значению, следует использовать вторую форму.

См. также

Ссылки

Класс transformer

Основные понятия

Библиотека асинхронных агентов

Другие ресурсы

Пошаговое руководство. Создание агента потоков данных

Пошаговое руководство. Создание сети обработки изображений