Библиотека асинхронных агентов
Библиотека асинхронных агентов (или просто библиотека агентов) предоставляет модель программирования, которая позволяет повысить надежность разработки приложений с поддержкой параллелизма. Библиотека агентов — это библиотека шаблонов C++, которая способствует модели программирования на основе субъектов и внутрипроцессного сообщения, передаваемого для грубого потока данных и задач конвейера. Библиотека агентов основана на компонентах планирования и управления ресурсами среды выполнения параллелизма.
Модель программирования
Библиотека агентов предоставляет альтернативы общему состоянию, позволяя подключать изолированные компоненты через асинхронную модель связи, основанную на потоке данных, а не потоке управления. Поток данных относится к модели программирования, в которой вычисления выполняются при наличии всех необходимых данных; Поток управления относится к модели программирования, в которой вычисления выполняются в предопределенном порядке.
Модель программирования на основе потоков данных связана с понятием передача сообщений, так как в этой модели независимые компоненты программы взаимодействуют друг с другом посредством отправки сообщений.
Библиотека агентов состоит из трех компонентов: асинхронных агентов, асинхронных блоков сообщений и функций передачи сообщений. Агенты поддерживают состояние и используют блоки сообщений и функции передачи сообщений для взаимодействия друг с другом и внешними компонентами. Функции передачи сообщений позволяют агентам отправлять и получать сообщения из внешних компонентов. Асинхронные блоки сообщений содержат сообщения и позволяют агентам взаимодействовать синхронизированным образом.
На следующем рисунке показано, как два агента используют блоки сообщений и функции передачи сообщений для обмена данными. На этом рисунке agent1
отправляется сообщение agent2
с помощью функции параллелизма::send и объекта concurrency::unbounded_buffer . agent2
использует функцию параллелизма::receive для чтения сообщения. agent2
использует тот же метод для отправки сообщения agent1
в . Дефисированные стрелки представляют поток данных между агентами. Сплошные стрелки подключают агентов к блокам сообщений, с которыми они записывают или считываются.
Пример кода, реализующий эту иллюстрацию, показан далее в этом разделе.
Модель программирования агента имеет несколько преимуществ по сравнению с другими механизмами параллелизма и синхронизации, например событиями. Одним из преимуществ является то, что с помощью передачи сообщений для передачи изменений состояния между объектами можно изолировать доступ к общим ресурсам и тем самым повысить масштабируемость. Преимущество передачи сообщений заключается в том, что он связывает синхронизацию с данными вместо привязки его к внешнему объекту синхронизации. Это упрощает передачу данных между компонентами и может устранять ошибки программирования в приложениях.
Когда следует использовать библиотеку агентов
Используйте библиотеку агентов при наличии нескольких операций, которые должны взаимодействовать друг с другом асинхронно. Блоки сообщений и функции передачи сообщений позволяют создавать параллельные приложения, не требуя механизмов синхронизации, таких как блокировки. Это позволяет сосредоточиться на логике приложения.
Модель программирования агента часто используется для создания конвейеров данных или сетей. Конвейер данных представляет собой ряд компонентов, каждый из которых выполняет определенную задачу, которая способствует более крупной цели. Каждый компонент в конвейере потока данных выполняет работу при получении сообщения от другого компонента. Результат этой работы передается другим компонентам в конвейере или сети. Компоненты могут использовать более подробные функции параллелизма из других библиотек, например библиотеку параллельных шаблонов (PPL).
Пример
В следующем примере показана иллюстрация, показанная ранее в этом разделе.
// basic-agents.cpp
// compile with: /EHsc
#include <agents.h>
#include <string>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
// This agent writes a string to its target and reads an integer
// from its source.
class agent1 : public agent
{
public:
explicit agent1(ISource<int>& source, ITarget<wstring>& target)
: _source(source)
, _target(target)
{
}
protected:
void run()
{
// Send the request.
wstringstream ss;
ss << L"agent1: sending request..." << endl;
wcout << ss.str();
send(_target, wstring(L"request"));
// Read the response.
int response = receive(_source);
ss = wstringstream();
ss << L"agent1: received '" << response << L"'." << endl;
wcout << ss.str();
// Move the agent to the finished state.
done();
}
private:
ISource<int>& _source;
ITarget<wstring>& _target;
};
// This agent reads a string to its source and then writes an integer
// to its target.
class agent2 : public agent
{
public:
explicit agent2(ISource<wstring>& source, ITarget<int>& target)
: _source(source)
, _target(target)
{
}
protected:
void run()
{
// Read the request.
wstring request = receive(_source);
wstringstream ss;
ss << L"agent2: received '" << request << L"'." << endl;
wcout << ss.str();
// Send the response.
ss = wstringstream();
ss << L"agent2: sending response..." << endl;
wcout << ss.str();
send(_target, 42);
// Move the agent to the finished state.
done();
}
private:
ISource<wstring>& _source;
ITarget<int>& _target;
};
int wmain()
{
// Step 1: Create two message buffers to serve as communication channels
// between the agents.
// The first agent writes messages to this buffer; the second
// agents reads messages from this buffer.
unbounded_buffer<wstring> buffer1;
// The first agent reads messages from this buffer; the second
// agents writes messages to this buffer.
overwrite_buffer<int> buffer2;
// Step 2: Create the agents.
agent1 first_agent(buffer2, buffer1);
agent2 second_agent(buffer1, buffer2);
// Step 3: Start the agents. The runtime calls the run method on
// each agent.
first_agent.start();
second_agent.start();
// Step 4: Wait for both agents to finish.
agent::wait(&first_agent);
agent::wait(&second_agent);
}
В примере получается следующий вывод.
agent1: sending request...
agent2: received 'request'.
agent2: sending response...
agent1: received '42'.
В следующих разделах описаны функциональные возможности, используемые в этом примере.
См. также
Асинхронные агенты
Описывает роль асинхронных агентов в решении более крупных вычислительных задач.
Асинхронные блоки сообщений
Описывает различные типы блоков сообщений, предоставляемые библиотекой агентов.
Функции передачи сообщений
Описывает различные процедуры передачи сообщений, предоставляемые библиотекой агентов.
Практическое руководство. Реализация различных шаблонов "источник-приемник"
Описывает, как реализовать шаблон производителя-потребителя в приложении.
Практическое руководство. Предоставление рабочих функций классам call и transformer
Иллюстрирует несколько способов предоставления рабочих функций классам параллелизма::call и параллелизма::преобразователя .
Практическое руководство. Использование преобразователя в конвейере данных
Показывает, как использовать класс параллелизма::преобразователя в конвейере данных.
Практическое руководство. Выбор среди завершенных задач
Показывает, как использовать классы параллелизма::choice и concurrency::join , чтобы выбрать первую задачу для выполнения алгоритма поиска.
Практическое руководство. Отправка сообщений через определенные интервалы
Показывает, как использовать класс параллелизма::timer для отправки сообщения через регулярный интервал.
Практическое руководство. Использование фильтра блоков сообщений
Демонстрирует использование фильтра для включения асинхронного блока сообщений для принятия или отклонения сообщений.
Библиотека параллельных шаблонов
Описывает использование различных параллельных шаблонов, таких как параллельные алгоритмы в приложениях.
Среда выполнения с параллелизмом
Описывает среду выполнения с параллелизмом, которая упрощает процесс параллельного программирования и содержит ссылки на соответствующие разделы.