Асинхронные блоки сообщений

Библиотека агентов предоставляет несколько типов блоков сообщений, которые позволяют распространять сообщения между компонентами приложения в потокобезопасном режиме. Эти типы блоков сообщений часто используются с различными подпрограммами передачи сообщений, такими как параллелизм::send, concurrency::asend, concurrency::receive и concurrency::try_receive. Дополнительные сведения о подпрограммах передачи сообщений, определенных библиотекой агентов, см. в разделе "Функции передачи сообщений".

Разделы

Этот раздел состоит из следующих подразделов.

Источники и целевые блоки

Источники и целевые объекты являются двумя важными участниками передачи сообщений. Источник ссылается на конечную точку связи, которая отправляет сообщения. Целевой объект ссылается на конечную точку связи, которая получает сообщения. Вы можете рассматривать источник как конечную точку, из которую вы читаете и целевой объект как конечную точку, в которую вы записываете. Приложения подключают источники и целевые объекты вместе для формирования сетей обмена сообщениями.

Библиотека агентов использует два абстрактных класса для представления источников и целевых объектов: параллелизм::ISource и параллелизм::ITarget. Типы блоков сообщений, которые действуют как источники, производные от ISource; типы блоков сообщений, которые действуют в качестве целевых объектов, производных от ITarget. Типы блоков сообщений, которые действуют как источники и целевые объекты, производные от обоих ISource и ITarget.

[В начало]

Распространение сообщений

Распространение сообщений — это действие отправки сообщения из одного компонента в другой. Если блок сообщения предлагается, он может принять, отклонить или отложить это сообщение. Каждый тип блока сообщений хранит и передает сообщения разными способами. Например, unbounded_buffer класс хранит неограниченное количество сообщений, overwrite_buffer класс хранит одно сообщение одновременно, а класс преобразователя сохраняет измененную версию каждого сообщения. Эти типы блоков сообщений подробно описаны далее в этом документе.

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

Библиотека агентов позволяет блокам сообщений асинхронно или синхронно передавать сообщения. При передаче сообщения блоку сообщения синхронно, например с помощью send функции, среда выполнения блокирует текущий контекст, пока целевой блок не принимает или отклоняет сообщение. При передаче сообщения в блок сообщений асинхронно, например с помощью asend функции, среда выполнения предлагает сообщение целевому объекту, а если целевой объект принимает сообщение, среда выполнения планирует асинхронную задачу, которая распространяет сообщение получателю. Среда выполнения использует упрощенные задачи для совместного распространения сообщений. Дополнительные сведения о упрощенных задачах см. в разделе Планировщик задач.

Приложения подключают источники и целевые объекты вместе для формирования сетей обмена сообщениями. Как правило, вы связываете сеть и вызываете send или asend передаете данные в сеть. Чтобы подключить блок исходного сообщения к целевому объекту , вызовите метод параллелизма::ISource::link_target . Чтобы отключить исходный блок от целевого объекта, вызовите метод concurrency::ISource::unlink_target . Чтобы отключить исходный блок от всех его целевых объектов, вызовите метод параллелизма::ISource::unlink_targets . Если один из стандартных типов блоков сообщений покидает область или уничтожается, он автоматически отключается от любых целевых блоков. Некоторые типы блоков сообщений ограничивают максимальное количество целевых объектов, в которые они могут записываться. В следующем разделе описываются ограничения, которые применяются к предопределенным типам блоков сообщений.

[В начало]

Общие сведения о типах блоков сообщений

В следующей таблице кратко описывается роль важных типов блоков сообщений.

unbounded_buffer
Хранит очередь сообщений.

overwrite_buffer
Сохраняет одно сообщение, которое можно записать в несколько раз и прочитать.

single_assignment
Сохраняет одно сообщение, которое можно записывать в один раз и читать с нескольких раз.

call
Выполняет работу при получении сообщения.

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

choice
Выбирает первое доступное сообщение из набора источников.

присоединение и многотипное соединение
Дождитесь получения всех сообщений из набора источников, а затем объедините сообщения в одно сообщение для другого блока сообщений.

Таймер
Отправляет сообщение целевому блоку через регулярный интервал.

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

  • Тип распространения: является ли блок сообщения источником данных, приемником данных или обоими.

  • Порядок сообщений: поддерживает ли блок сообщения исходный порядок отправки или получения сообщений. Каждый предопределенный тип блока сообщений сохраняет исходный порядок отправки или получения сообщений.

  • Число источников: максимальное количество источников, из которых блок сообщений может считываться.

  • Число целевых объектов: максимальное число целевых объектов, в которые блок сообщений может записываться.

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

Тип блока сообщений Тип распространения (источник, целевой объект или оба) Упорядочение сообщений (упорядочено или неупорядочено) Число источников Число целевых объектов
unbounded_buffer Оба Занято Неограниченный Неограниченный
overwrite_buffer Оба Занято Неограниченный Неограниченный
single_assignment Оба Занято Неограниченный Неограниченный
call Назначение Занято Неограниченный Н/Д
transformer Оба Занято Неограниченный 1
choice Оба Занято 10 1
join Оба Занято Неограниченный 1
multitype_join Оба Занято 10 1
timer Оригинал Н/Д Н/Д 1

В следующих разделах подробно описаны типы блоков сообщений.

[В начало]

Класс unbounded_buffer

Класс параллелизма::unbounded_buffer представляет структуру асинхронного обмена сообщениями общего назначения. В этом классе хранится очередь сообщений типа «первым вошел — первым вышел» (FIFO), в которую могут записывать данные несколько источников и из которой могут читать данные несколько целевых объектов. Когда целевой объект получает сообщение от unbounded_buffer объекта, это сообщение удаляется из очереди сообщений. Поэтому, хотя объект может иметь несколько целевых объектов, только один целевой unbounded_buffer объект получит каждое сообщение. Класс unbounded_buffer удобен, если нужно передать несколько сообщений другому компоненту и этот компонент должен принять каждое сообщение.

Пример

В следующем примере показана базовая структура работы с классом unbounded_buffer . Этот пример отправляет три значения unbounded_buffer в объект, а затем считывает эти значения обратно из одного объекта.

// unbounded_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create an unbounded_buffer object that works with
   // int data.
   unbounded_buffer<int> items;

   // Send a few items to the unbounded_buffer object.
   send(items, 33);
   send(items, 44);
   send(items, 55);

   // Read the items from the unbounded_buffer object and print
   // them to the console.
   wcout << receive(items) << endl;
   wcout << receive(items) << endl;
   wcout << receive(items) << endl;
}

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

334455

Полный пример использования unbounded_buffer класса см. в разделе "Практическое руководство. Реализация различных шаблонов потребителей-производителей".

[В начало]

Класс overwrite_buffer

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

Класс overwrite_buffer полезен, если требуется передать несколько сообщений другому компоненту, но этот компонент должен иметь только последнее значение. Этот класс также может оказаться полезным при необходимости широковещательной передачи сообщения нескольким компонентам.

Пример

В следующем примере показана базовая структура работы с классом overwrite_buffer . В этом примере три значения отправляются overwrite _buffer в объект, а затем считываются текущее значение из одного объекта три раза. Этот пример аналогичен примеру unbounded_buffer для класса. overwrite_buffer Однако класс сохраняет только одно сообщение. Кроме того, среда выполнения не удаляет сообщение из overwrite_buffer объекта после его чтения.

// overwrite_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create an overwrite_buffer object that works with
   // int data.
   overwrite_buffer<int> item;

   // Send a few items to the overwrite_buffer object.
   send(item, 33);
   send(item, 44);
   send(item, 55);

   // Read the current item from the overwrite_buffer object and print
   // it to the console three times.
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
}

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

555555

Полный пример использования overwrite_buffer класса см. в разделе "Практическое руководство. Реализация различных шаблонов потребителей-производителей".

[В начало]

Класс single_assignment

Класс параллелизма::single_assignment похож на overwrite_buffer класс, за исключением того, что single_assignment объект можно записать только один раз. Как и в случае с классом overwrite_buffer, когда целевой объект получает сообщение от объекта single_assignment, это сообщение не удаляется. Поэтому копию сообщения могут получить несколько целевых объектов. Класс single_assignment полезен, если вы хотите транслировать одно сообщение нескольким компонентам.

Пример

В следующем примере показана базовая структура работы с классом single_assignment . В этом примере три значения отправляются single_assignment в объект, а затем считываются текущее значение из одного объекта три раза. Этот пример аналогичен примеру overwrite_buffer для класса. Хотя оба overwrite_buffersingle_assignment класса хранят одно сообщение, single_assignment класс можно записать только один раз.

// single_assignment-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create an single_assignment object that works with
   // int data.
   single_assignment<int> item;

   // Send a few items to the single_assignment object.
   send(item, 33);
   send(item, 44);
   send(item, 55);

   // Read the current item from the single_assignment object and print
   // it to the console three times.
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
}

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

333333

Полный пример использования single_assignment класса см. в пошаговом руководстве. Реализация фьючерсов.

[В начало]

Класс call

Класс concurrency::call выступает в качестве приемника сообщений, выполняющего рабочую функцию при получении данных. Эта рабочая функция может быть лямбда-выражением, объектом функции или указателем функции. call Объект ведет себя не так, как обычный вызов функции, так как он действует параллельно с другими компонентами, отправляющими сообщения в него. call Если объект выполняет работу при получении сообщения, он добавляет это сообщение в очередь. Каждый call объект обрабатывает сообщения в очереди в порядке их получения.

Пример

В следующем примере показана базовая структура работы с классом call . В этом примере создается call объект, который выводит каждое значение, которое оно получает в консоль. Затем этот пример отправляет три значения объекту call . call Так как объект обрабатывает сообщения в отдельном потоке, этот пример также использует переменную счетчика и объект события, чтобы убедиться, что call объект обрабатывает все сообщения перед возвратом wmain функции.

// call-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // An event that is set when the call object receives all values.
   event received_all;

   // Counts the 
   long receive_count = 0L;
   long max_receive_count = 3L;

   // Create an call object that works with int data.
   call<int> target([&received_all,&receive_count,max_receive_count](int n) {
      // Print the value that the call object receives to the console.
      wcout << n << endl;
      
      // Set the event when all messages have been processed.
      if (++receive_count == max_receive_count)
         received_all.set();
   });

   // Send a few items to the call object.
   send(target, 33);
   send(target, 44);
   send(target, 55);

   // Wait for the call object to process all items.
   received_all.wait();
}

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

334455

Полный пример использования call класса см. в разделе "Практическое руководство. Предоставление рабочих функций классам вызова и преобразователя".

[В начало]

Класс transformer

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

Класс transformer отправляет сообщение одному целевому объекту. Если параметр в конструкторе задан_PTarget, можно позже указать целевой объект, вызвав метод параллелизма::link_target.NULL

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

Пример

В следующем примере показана базовая структура работы с классом transformer . В этом примере создается transformer объект, который несколько входных int значений на 0,33, чтобы создать double значение в виде выходных данных. Затем этот пример получает преобразованные значения из того же transformer объекта и выводит их в консоль.

// transformer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create an transformer object that receives int data and 
   // sends double data.
   transformer<int, double> third([](int n) {
      // Return one-third of the input value.
      return n * 0.33;
   });

   // Send a few items to the transformer object.
   send(third, 33);
   send(third, 44);
   send(third, 55);

   // Read the processed items from the transformer object and print
   // them to the console.
   wcout << receive(third) << endl;
   wcout << receive(third) << endl;
   wcout << receive(third) << endl;
}

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

10.8914.5218.15

Полный пример использования transformer класса см. в разделе "Практическое руководство. Использование преобразователя в конвейере данных".

[В начало]

Класс choice

Класс параллелизма::choice выбирает первое доступное сообщение из набора источников. Класс choice представляет механизм потока управления вместо механизма потока данных (в разделе "Библиотека асинхронных агентов" описывает различия между потоком данных и потоком управления).

Чтение из объекта выбора напоминает вызов функции WaitForMultipleObjects API Windows, если он имеет bWaitAll параметр.FALSE choice Однако класс привязывает данные к самому событию, а не к внешнему объекту синхронизации.

Как правило, класс используется choice вместе с функцией параллелизма::receive для управления потоком управления в приложении. choice Используйте класс, когда нужно выбрать между буферами сообщений, имеющими разные типы. single_assignment Используйте класс, когда нужно выбрать один и тот же тип буферов сообщений.

Порядок связывания источников с choice объектом важен, так как он может определить, какое сообщение выбрано. Например, рассмотрим случай, когда вы связываете несколько буферов сообщений, которые уже содержат сообщение с choice объектом. Объект choice выбирает сообщение из первого источника, к которому он связан. После связывания всех источников объект сохраняет порядок, choice в котором каждый источник получает сообщение.

Пример

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

// choice-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Although the following thee message blocks are written to one time only, 
   // this example illustrates the fact that the choice class works with 
   // different message block types.

   // Holds the 35th Fibonacci number.
   single_assignment<int> fib35;
   // Holds the 37th Fibonacci number.
   overwrite_buffer<int> fib37;
   // Holds half of the 42nd Fibonacci number.
   unbounded_buffer<double> half_of_fib42;   

   // Create a choice object that selects the first single_assignment 
   // object that receives a value.
   auto select_one = make_choice(&fib35, &fib37, &half_of_fib42);

   // Execute a few lengthy operations in parallel. Each operation sends its 
   // result to one of the single_assignment objects.
   parallel_invoke(
      [&fib35] { send(fib35, fibonacci(35)); },
      [&fib37] { send(fib37, fibonacci(37)); },
      [&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
   );

   // Print a message that is based on the operation that finished first.
   switch (receive(select_one))
   {
   case 0:
      wcout << L"fib35 received its value first. Result = " 
            << receive(fib35) << endl;
      break;
   case 1:
      wcout << L"fib37 received its value first. Result = " 
            << receive(fib37) << endl;
      break;
   case 2:
      wcout << L"half_of_fib42 received its value first. Result = " 
            << receive(half_of_fib42) << endl;
      break;
   default:
      wcout << L"Unexpected." << endl;
      break;
   }
}

В этом примере создаются следующие примеры выходных данных:

fib35 received its value first. Result = 9227465

Так как задача, вычисляющая 35-й номер Fibonacci, не гарантируется, что сначала завершится, выходные данные этого примера могут отличаться.

В этом примере используется алгоритм параллелизма::p arallel_invoke для параллельного вычисления чисел Fibonacci. Дополнительные сведения см. в parallel_invokeразделе "Параллельные алгоритмы".

Полный пример использования choice класса см. в разделе "Практическое руководство. Выбор среди завершенных задач".

[В начало]

Классы join и multitype_join

Классы параллелизма::join и concurrency::multitype_join позволяют ждать, пока каждый член набора источников получит сообщение. Класс join действует на исходных объектах, имеющих общий тип сообщения. Класс multitype_join действует на исходных объектах, которые могут иметь разные типы сообщений.

Чтение из join объекта multitype_join или объекта напоминает вызов функции WaitForMultipleObjects API Windows, если он имеет bWaitAll параметр TRUE. Однако, как choice и объект, join и multitype_join объекты используют механизм событий, который привязывает данные к самому событию, а не к внешнему объекту синхронизации.

Чтение из join объекта создает объект std::vector . Чтение из multitype_join объекта создает объект std::кортеж . Элементы отображаются в этих объектах в том же порядке, что и соответствующие исходные буферы, связаны с join объектом или multitype_join объектом. Так как порядок связывания исходных буферов с join объектом или multitype_join объектом связан с порядком элементов в результирующем vector или tuple объекте, рекомендуется не связывать существующий исходный буфер из соединения. Это может привести к неуказаенному поведению.

Жадные и не жадные соединения

Классы joinmultitype_join поддерживают концепцию жадных и не жадных соединений. Жадное соединение принимает сообщение из каждого из источников по мере того как сообщения становятся доступными до тех пор, пока все сообщения не будут доступны. Не жадное соединение получает сообщения на двух этапах. Во-первых, не жадное соединение ожидает, пока оно не будет предложено сообщение от каждого из его источников. Во-вторых, после того как все исходные сообщения доступны, не жадное присоединение пытается зарезервировать каждый из этих сообщений. Если он может зарезервировать каждое сообщение, он потребляет все сообщения и распространяет их на целевой объект. В противном случае он освобождает или отменяет резервирование сообщений и снова ожидает получения каждого источника сообщения.

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

Пример

В следующем примере показана базовая структура работы с классом join . В этом примере функция параллелизма::make_join используется для создания join объекта, получающего от трех single_assignment объектов. В этом примере вычисляются различные числа Fibonacci, хранятся каждый результат в другом single_assignment объекте, а затем выводится в консоль каждый результат, который join содержит объект. Этот пример аналогичен примеру для choice класса, за исключением того, что join класс ожидает получения сообщения во всех блоках исходного сообщения.

// join-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Holds the 35th Fibonacci number.
   single_assignment<int> fib35;
   // Holds the 37th Fibonacci number.
   single_assignment<int> fib37;
   // Holds half of the 42nd Fibonacci number.
   single_assignment<double> half_of_fib42;   

   // Create a join object that selects the values from each of the
   // single_assignment objects.
   auto join_all = make_join(&fib35, &fib37, &half_of_fib42);

   // Execute a few lengthy operations in parallel. Each operation sends its 
   // result to one of the single_assignment objects.
   parallel_invoke(
      [&fib35] { send(fib35, fibonacci(35)); },
      [&fib37] { send(fib37, fibonacci(37)); },
      [&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
   );

   auto result = receive(join_all);
   wcout << L"fib35 = " << get<0>(result) << endl;
   wcout << L"fib37 = " << get<1>(result) << endl;
   wcout << L"half_of_fib42 = " << get<2>(result) << endl;
}

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

fib35 = 9227465fib37 = 24157817half_of_fib42 = 1.33957e+008

В этом примере используется алгоритм параллелизма::p arallel_invoke для параллельного вычисления чисел Fibonacci. Дополнительные сведения см. в parallel_invokeразделе "Параллельные алгоритмы".

Полные примеры использования join класса см. в разделе "Практическое руководство. Выбор между завершенными задачами и пошаговое руководство. Использование соединения для предотвращения взаимоблокировки".

[В начало]

Класс timer

Класс параллелизма::timer выступает в качестве источника сообщения. timer Объект отправляет сообщение целевому объекту после истечения указанного периода времени. Класс timer полезен, если необходимо отложить отправку сообщения или отправить сообщение через регулярный интервал.

Класс timer отправляет сообщение только одному целевому объекту. Если параметр в конструкторе задан_PTarget, можно позже указать целевой объект, вызвав метод параллелизма::ISource::link_target.NULL

Объект timer может повторяться или не повторяться. Чтобы создать повторяющийся таймер, передайте true параметр _Repeating при вызове конструктора. В противном случае передайте false параметр для _Repeating создания таймера, не повторяющегося. Если таймер повторяется, он отправляет одно и то же сообщение в целевой объект после каждого интервала.

Библиотека агентов создает timer объекты в состоянии, отличном от запуска. Чтобы запустить объект таймера, вызовите метод параллелизма::timer::start . Чтобы остановить timer объект, удалите объект или вызовите метод параллелизма::timer::stop . Чтобы приостановить повторяющийся таймер, вызовите метод параллелизма::timer::p ause .

Пример

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

// timer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Create a call object that prints characters that it receives 
   // to the console.
   call<wchar_t> print_character([](wchar_t c) {
      wcout << c;
   });

   // Create a timer object that sends the period (.) character to 
   // the call object every 100 milliseconds.
   timer<wchar_t> progress_timer(100u, L'.', &print_character, true);

   // Start the timer.
   wcout << L"Computing fib(42)";
   progress_timer.start();

   // Compute the 42nd Fibonacci number.
   int fib42 = fibonacci(42);

   // Stop the timer and print the result.
   progress_timer.stop();
   wcout << endl << L"result is " << fib42 << endl;
}

В этом примере создаются следующие примеры выходных данных:

Computing fib(42)..................................................result is 267914296

Полный пример использования timer класса см. в разделе "Практическое руководство. Отправка сообщения по регулярному интервалу".

[В начало]

Фильтрация сообщений

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

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

// filter-function.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create an unbounded_buffer object that uses a filter
   // function to accept only even numbers.
   unbounded_buffer<int> accept_evens(
      [](int n) {
         return (n%2) == 0;
      });

   // Send a few values to the unbounded_buffer object.
   unsigned int accept_count = 0;
   for (int i = 0; i < 10; ++i)
   {
      // The asend function returns true only if the target
      // accepts the message. This enables us to determine
      // how many elements are stored in the unbounded_buffer
      // object.
      if (asend(accept_evens, i))
      {
         ++accept_count;
      }
   }

   // Print to the console each value that is stored in the 
   // unbounded_buffer object. The unbounded_buffer object should
   // contain only even numbers.
   while (accept_count > 0)
   {
      wcout << receive(accept_evens) << L' ';
      --accept_count;
   }
}

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

0 2 4 6 8

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

bool (T)
bool (T const &)

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

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

[В начало]

Резервирование сообщений

Резервирование сообщений позволяет блоку сообщений зарезервировать сообщение для последующего использования. Как правило, резервирование сообщений не используется напрямую. Однако понимание резервирования сообщений поможет лучше понять поведение некоторых предопределенных типов блоков сообщений.

Рассмотрим не жадные и жадные соединения. Оба из них используют резервирование сообщений для резервирования сообщений для последующего использования. Описанное ранее соединение, не жадное соединение получает сообщения на двух этапах. На первом этапе не жадный join объект ожидает получения сообщения каждым из его источников. Затем не жадное соединение пытается зарезервировать каждое из этих сообщений. Если он может зарезервировать каждое сообщение, он потребляет все сообщения и распространяет их на целевой объект. В противном случае он освобождает или отменяет резервирование сообщений и снова ожидает получения каждого источника сообщения.

Жадное соединение, которое также считывает входные сообщения из ряда источников, использует резервирование сообщений для чтения дополнительных сообщений, пока ожидает получения сообщения из каждого источника. Например, рассмотрим жадное соединение, которое получает сообщения из блоков A сообщений и B. Если жадное соединение получает два сообщения из B, но еще не получено сообщение от A, жадное соединение сохраняет уникальный идентификатор сообщения для второго сообщения B. Когда жадное соединение получает сообщение от A и распространяет эти сообщения, он использует сохраненный идентификатор сообщения, чтобы узнать, доступен ли второй из них B .

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

[В начало]

См. также

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