Асинхронные блоки сообщений
Библиотека агентов предоставляет несколько типов блоков сообщений, которые позволяют распространять сообщения между компонентами приложения в потокобезопасном режиме. Эти типы блоков сообщений часто используются с различными подпрограммами передачи сообщений, такими как параллелизм::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_buffer
single_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
объекте, рекомендуется не связывать существующий исходный буфер из соединения. Это может привести к неуказаенному поведению.
Жадные и не жадные соединения
Классы join
multitype_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
.
При реализации собственных типов блоков сообщений можно использовать резервирование сообщений. Пример создания типа пользовательского блока сообщений см. в пошаговом руководстве. Создание настраиваемого блока сообщений.
[В начало]