Пошаговое руководство. Создание приложения на основе агента
В этом разделе описывается создание базового приложения на основе агента. В этом пошаговом руководстве можно создать агент, который считывает данные из текстового файла асинхронно. Приложение использует алгоритм adler-32 проверка sum для вычисления проверка сумы содержимого этого файла.
Необходимые компоненты
Для выполнения этого пошагового руководства необходимо понять следующие разделы:
Разделы
В этом пошаговом руководстве показано, как выполнить следующие задачи:
Создание консольного приложения
В этом разделе показано, как создать консольное приложение C++, которое ссылается на файлы заголовков, которые будет использоваться программой. Начальные шаги зависят от используемой версии Visual Studio. Чтобы ознакомиться с документацией по предпочтительной версии Visual Studio, используйте селектор Версия. Он находится в верхней части оглавления на этой странице.
Создание консольного приложения C++ в Visual Studio
В главном меню выберите Файл >Создать >Проект, чтобы открыть диалоговое окно Создание проекта.
В верхней части диалогового окна задайте для параметра Язык значение C++, для параметра Платформа значение Windows, а для Типа проекта — Консоль.
В отфильтрованном списке типов проектов щелкните Консольное приложение, а затем нажмите кнопку Далее. На следующей странице введите
BasicAgent
имя проекта и укажите расположение проекта при необходимости.Нажмите кнопку Создать, чтобы создать проект.
Щелкните правой кнопкой мыши узел проекта в Обозреватель решений и выберите пункт "Свойства". В разделе свойств>конфигурации C/C++>Precompiled Headers Precompiled headers (Предварительно скомпилированные заголовки>) нажмите кнопку Create (Создать).
Создание консольного приложения C++ в Visual Studio 2017 и более ранних версиях
В меню "Файл" нажмите кнопку "Создать", а затем выберите "Проект", чтобы отобразить диалоговое окно "Новый проект".
В диалоговом окне "Создать проект" выберите узел Visual C++ в области "Типы проектов" и выберите консольное приложение Win32 на панели "Шаблоны". Введите имя проекта, например, и нажмите кнопку "ОК",
BasicAgent
чтобы отобразить мастер консольного приложения Win32.В диалоговом окне мастера консольного приложения Win32 нажмите кнопку "Готово".
Обновление файла заголовка
В файле pch.h (stdafx.h в Visual Studio 2017 и более ранних версиях) добавьте следующий код:
#include <agents.h>
#include <string>
#include <iostream>
#include <algorithm>
Файл заголовка agent.h содержит функциональные возможности класса concurrency::agent.
Проверка приложения
Наконец, убедитесь, что приложение было успешно создано путем создания и запуска приложения. Чтобы создать приложение, в меню "Сборка" нажмите кнопку "Создать решение". Если приложение успешно строится, запустите приложение, нажав кнопку "Начать отладку " в меню отладки .
[В начало]
Создание класса file_reader
В этом разделе показано, как создать file_reader
класс. Среда выполнения планирует выполнение каждого агента в собственном контексте. Таким образом, можно создать агент, который выполняет синхронную работу, но взаимодействует с другими компонентами асинхронно. Класс file_reader
считывает данные из заданного входного файла и отправляет данные из этого файла в заданный целевой компонент.
Создание класса file_reader
Добавьте новый файл заголовка C++ в проект. Для этого щелкните правой кнопкой мыши узел "Файлы заголовков" в Обозреватель решений, нажмите кнопку "Добавить" и выберите пункт "Создать элемент". В области "Шаблоны" выберите файл заголовка (.h). В диалоговом окне "Добавить новый элемент" введите
file_reader.h
поле "Имя" и нажмите кнопку "Добавить".В file_reader.h добавьте следующий код.
#pragma once
В file_reader.h создайте класс, который называется
file_reader
производным отagent
.class file_reader : public concurrency::agent { public: protected: private: };
Добавьте следующие члены данных в
private
раздел класса.std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
Элемент
_file_name
— это имя файла, считываемое агентом. Элемент_target
является объектом параллелизма::ITarget , в который агент записывает содержимое файла. Член_error
содержит любую ошибку, возникающую во время существования агента.Добавьте следующий код для
file_reader
конструкторов вpublic
разделfile_reader
класса.explicit file_reader(const std::string& file_name, concurrency::ITarget<std::string>& target) : _file_name(file_name) , _target(target) { } explicit file_reader(const std::string& file_name, concurrency::ITarget<std::string>& target, concurrency::Scheduler& scheduler) : agent(scheduler) , _file_name(file_name) , _target(target) { } explicit file_reader(const std::string& file_name, concurrency::ITarget<std::string>& target, concurrency::ScheduleGroup& group) : agent(group) , _file_name(file_name) , _target(target) { }
Каждая перегрузка конструктора задает
file_reader
элементы данных. Вторая и третья перегрузка конструктора позволяет приложению использовать конкретный планировщик с агентом. Первая перегрузка использует планировщик по умолчанию с агентом.Добавьте метод в
get_error
общедоступный разделfile_reader
класса.bool get_error(std::exception& e) { return try_receive(_error, e); }
Метод
get_error
получает любую ошибку, возникающую во время существования агента.Реализуйте метод параллелизма::agent::run в
protected
разделе класса.void run() { FILE* stream; try { // Open the file. if (fopen_s(&stream, _file_name.c_str(), "r") != 0) { // Throw an exception if an error occurs. throw std::exception("Failed to open input file."); } // Create a buffer to hold file data. char buf[1024]; // Set the buffer size. setvbuf(stream, buf, _IOFBF, sizeof buf); // Read the contents of the file and send the contents // to the target. while (fgets(buf, sizeof buf, stream)) { asend(_target, std::string(buf)); } // Send the empty string to the target to indicate the end of processing. asend(_target, std::string("")); // Close the file. fclose(stream); } catch (const std::exception& e) { // Send the empty string to the target to indicate the end of processing. asend(_target, std::string("")); // Write the exception to the error buffer. send(_error, e); } // Set the status of the agent to agent_done. done(); }
Метод run
открывает файл и считывает данные из него. Метод run
использует обработку исключений для записи ошибок, возникающих во время обработки файлов.
Каждый раз, когда этот метод считывает данные из файла, он вызывает функцию параллелизма::asend для отправки этих данных в целевой буфер. Он отправляет пустую строку в целевой буфер, чтобы указать конец обработки.
В следующем примере показано полное содержимое file_reader.h.
#pragma once
class file_reader : public concurrency::agent
{
public:
explicit file_reader(const std::string& file_name,
concurrency::ITarget<std::string>& target)
: _file_name(file_name)
, _target(target)
{
}
explicit file_reader(const std::string& file_name,
concurrency::ITarget<std::string>& target,
concurrency::Scheduler& scheduler)
: agent(scheduler)
, _file_name(file_name)
, _target(target)
{
}
explicit file_reader(const std::string& file_name,
concurrency::ITarget<std::string>& target,
concurrency::ScheduleGroup& group)
: agent(group)
, _file_name(file_name)
, _target(target)
{
}
// Retrieves any error that occurs during the life of the agent.
bool get_error(std::exception& e)
{
return try_receive(_error, e);
}
protected:
void run()
{
FILE* stream;
try
{
// Open the file.
if (fopen_s(&stream, _file_name.c_str(), "r") != 0)
{
// Throw an exception if an error occurs.
throw std::exception("Failed to open input file.");
}
// Create a buffer to hold file data.
char buf[1024];
// Set the buffer size.
setvbuf(stream, buf, _IOFBF, sizeof buf);
// Read the contents of the file and send the contents
// to the target.
while (fgets(buf, sizeof buf, stream))
{
asend(_target, std::string(buf));
}
// Send the empty string to the target to indicate the end of processing.
asend(_target, std::string(""));
// Close the file.
fclose(stream);
}
catch (const std::exception& e)
{
// Send the empty string to the target to indicate the end of processing.
asend(_target, std::string(""));
// Write the exception to the error buffer.
send(_error, e);
}
// Set the status of the agent to agent_done.
done();
}
private:
std::string _file_name;
concurrency::ITarget<std::string>& _target;
concurrency::overwrite_buffer<std::exception> _error;
};
[В начало]
Использование класса file_reader в приложении
В этом разделе показано, как использовать file_reader
класс для чтения содержимого текстового файла. В нем также показано, как создать объект параллелизма::call, который получает эти данные файла и вычисляет его Adler-32 проверка sum.
Использование класса file_reader в приложении
В BasicAgent.cpp добавьте следующую
#include
инструкцию.#include "file_reader.h"
В BasicAgent.cpp добавьте следующие
using
директивы.using namespace concurrency; using namespace std;
_tmain
В функции создайте объект параллелизма::event, который сигнализирует о завершении обработки.event e;
call
Создайте объект, обновляющий проверка sum при получении данных.// The components of the Adler-32 sum. unsigned int a = 1; unsigned int b = 0; // A call object that updates the checksum when it receives data. call<string> calculate_checksum([&] (string s) { // If the input string is empty, set the event to signal // the end of processing. if (s.size() == 0) e.set(); // Perform the Adler-32 checksum algorithm. for_each(begin(s), end(s), [&] (char c) { a = (a + c) % 65521; b = (b + a) % 65521; }); });
Этот
call
объект также задаетevent
объект, когда он получает пустую строку, чтобы сигнализировать о завершении обработки.file_reader
Создайте объект, который считывает файл test.txt и записывает содержимое этого файла вcall
объект.file_reader reader("test.txt", calculate_checksum);
Запустите агент и дождитесь завершения.
reader.start(); agent::wait(&reader);
call
Дождитесь получения всех данных и завершения объекта.e.wait();
Проверьте средство чтения файлов для ошибок. Если ошибка не произошла, вычислите окончательную сумму Adler-32 и распечатайте сумму в консоли.
std::exception error; if (reader.get_error(error)) { wcout << error.what() << endl; } else { unsigned int adler32_sum = (b << 16) | a; wcout << L"Adler-32 sum is " << hex << adler32_sum << endl; }
В следующем примере показан полный файл BasicAgent.cpp.
// BasicAgent.cpp : Defines the entry point for the console application.
//
#include "pch.h" // Use stdafx.h in Visual Studio 2017 and earlier
#include "file_reader.h"
using namespace concurrency;
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
// An event object that signals the end of processing.
event e;
// The components of the Adler-32 sum.
unsigned int a = 1;
unsigned int b = 0;
// A call object that updates the checksum when it receives data.
call<string> calculate_checksum([&] (string s) {
// If the input string is empty, set the event to signal
// the end of processing.
if (s.size() == 0)
e.set();
// Perform the Adler-32 checksum algorithm.
for_each(begin(s), end(s), [&] (char c) {
a = (a + c) % 65521;
b = (b + a) % 65521;
});
});
// Create the agent.
file_reader reader("test.txt", calculate_checksum);
// Start the agent and wait for it to complete.
reader.start();
agent::wait(&reader);
// Wait for the call object to receive all data and complete.
e.wait();
// Check the file reader for errors.
// If no error occurred, calculate the final Adler-32 sum and print it
// to the console.
std::exception error;
if (reader.get_error(error))
{
wcout << error.what() << endl;
}
else
{
unsigned int adler32_sum = (b << 16) | a;
wcout << L"Adler-32 sum is " << hex << adler32_sum << endl;
}
}
[В начало]
Пример ввода
Это пример содержимого входного файла text.txt:
The quick brown fox
jumps
over the lazy dog
Образец вывода
При использовании с примером входных данных эта программа создает следующие выходные данные:
Adler-32 sum is fefb0d75
Отказоустойчивость
Чтобы предотвратить одновременный доступ к членам данных, рекомендуется добавить методы, которые выполняют работу в protected
класс или private
раздел. Добавьте только методы, отправляющие или получающие сообщения в агент или из агента в public
раздел класса.
Всегда вызывайте метод параллелизма::agent::d one , чтобы переместить агент в готовое состояние. Обычно этот метод вызывается перед возвратом run
из метода.
Дальнейшие действия
Другой пример приложения на основе агента см. в пошаговом руководстве. Использование соединения для предотвращения взаимоблокировки.
См. также
Библиотека асинхронных агентов
Асинхронные блоки сообщений
Функции передачи сообщений
Структуры данных синхронизации
Пошаговое руководство. Использование класса join для предотвращения взаимоблокировки
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по