Instruções passo a passo: criando um aplicativo com base no agente
Este tópico descreve como criar um aplicativo agente-baseado básico. Neste passo a passo, você pode criar um agente que lê dados de um arquivo de texto de forma assíncrona. O aplicativo usa o algoritmo de soma de verificação Adler-32 para calcular a soma de verificação do conteúdo desse arquivo.
Pré-requisitos
Você deve entender os tópicos a seguir para concluir este passo a passo:
Seções
Este passo a passo demonstra como executar as seguintes tarefas:
Criando o Aplicativo de Console
Criando a Classe file_reader
Usando a Classe file_reader no Aplicativo
Criando o Aplicativo de Console
Esta seção mostra como criar um aplicativo de console do Visual C++ que faz referência aos arquivos de cabeçalho que o programa usará.
Para criar um aplicativo Visual C++ usando o Assistente do Aplicativo de Console Win32
No menu de Arquivo , clique em Novo, e clique em Projeto para exibir a caixa de diálogo de Novo Projeto .
Na caixa de diálogo de Novo Projeto , selecione o nó de Visual C++ no painel de Tipos de projeto e selecione Aplicativo do Console Win32 no painel de Modelos . Digite um nome para o projeto, por exemplo, BasicAgent, e clique em OK para exibir Assistente do aplicativo de console do Win32.
Na caixa de diálogo de Assistente do aplicativo de console do Win32 , clique Concluir.
Em stdafx.h, adicione o seguinte código.
#include <agents.h> #include <string> #include <iostream> #include <algorithm>
O arquivo de cabeçalho agents.h contém a funcionalidade da classe de concurrency::agent .
Verifique se o aplicativo foi criado com êxito criando e executando o. Para compilar o aplicativo, no menu Compilar, clique em Compilar Solução. Se o aplicativo for compilado com êxito, execute-o clicando em Iniciar Depuração no menu Depurar.
[Superior]
Criando a Classe file_reader
Esta seção mostra como criar a classe de file_reader . O tempo de execução agenda cada agente para executar o trabalho em seu próprio contexto. Em virtude disso, você pode criar um o agent que execute o trabalho de forma síncrona, mas interage com outros componentes de forma assíncrona. A classe de file_reader ler dados de um arquivo de entrada especificado e envia os dados desse arquivo a um determinado componente de destino.
Para criar a classe file_reader
Adicionar um novo arquivo de cabeçalho C++ ao seu projeto. Para fazer isso, clique com o botão direito do mouse no nó de Arquivos de Cabeçalho em Gerenciador de Soluções, clique Adicionar, e clique em Novo Item. No painel de Modelos , **Cabeçalho Arquivo (.h)**selecione. Na caixa de diálogo de Adicionar novo item , digite file_reader.h na caixa de Nome e clique em Adicionar.
Em file_reader.h, adicione o seguinte código.
#pragma once
Em file_reader.h, crie uma classe chamada file_reader que deriva de agent.
class file_reader : public concurrency::agent { public: protected: private: };
Adicione os seguintes membros de dados na seção de private da classe.
std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
O membro de _file_name é o nome de arquivo que o agente lê. O membro de _target é um objeto de concurrency::ITarget que o agent grava o conteúdo do arquivo como. O membro de _error contém qualquer erro que ocorra durante a vida do agent.
Adicione o seguinte código para os construtores de file_reader a seção de public da classe de 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) { }
Cada sobrecarga de construtor define os membros de dados de file_reader . A segunda e a terceira sobrecarga de construtor permite que o seu aplicativo usar um agendador específico com seu agente. A primeira sobrecarga usa o agendador padrão com o agente.
Adicionar o método de get_error a seção pública da classe de file_reader .
bool get_error(std::exception& e) { return try_receive(_error, e); }
O método de get_error recupera qualquer erro que ocorra durante a vida do agent.
Implementar o método de concurrency::agent::run na seção de protected da classe.
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(); }
O método de run abre o arquivo e ler dados deles. O método de run usa a manipulação de exceção para capturar todos os erros que ocorrem durante o processamento de arquivo.
Cada vez que esse método lê dados do arquivo, chamará a função de concurrency::asend para enviar os dados ao buffer de destino. Envia a cadeia de caracteres vazia ao buffer de destino para indicar o final do processamento.
O exemplo a seguir mostra o conteúdo completo de 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;
};
[Superior]
Usando a Classe file_reader no Aplicativo
Esta seção mostra como usar a classe de file_reader para ler o conteúdo de um arquivo de texto. Também mostra como criar um objeto de concurrency::call que recebe os dados de arquivo e calcula a soma de verificação Adler-32.
Para usar a classe file_reader no aplicativo
Em BasicAgent.cpp, adicione a seguinte instrução de #include .
#include "file_reader.h"
Em BasicAgent.cpp, adicione as políticas de using .
using namespace concurrency; using namespace std;
Na função de _tmain , crie um objeto de concurrency::event que sinalize o final do processamento.
event e;
Crie um objeto de call que atualiza a soma de verificação quando o recebe dados.
// 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; }); });
Esse objeto de call também define o objeto de event quando o recebe a cadeia de caracteres vazia para sinalizar o final do processamento.
Crie um objeto de file_reader que a leitura do arquivo test.txt e grava o conteúdo desse arquivo ao objeto de call .
file_reader reader("test.txt", calculate_checksum);
Inicie o agente e espere-o para concluir.
reader.start(); agent::wait(&reader);
Aguarde o objeto de call para receber todos os dados e para concluir-los.
e.wait();
Verifique o leitor do arquivo para erros. Se nenhum erro ocorreu, calcula a soma Adler-32 final e imprima a soma no 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; }
O exemplo a seguir mostra o arquivo completo de BasicAgent.cpp.
// BasicAgent.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#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;
}
}
[Superior]
Entrada de Exemplo
Este é o conteúdo de exemplo do arquivo de entrada: text.txt
Saída de Exemplo
Quando usado com a entrada de exemplo, esse programa gerencia a seguinte saída:
Programação robusta
Para evitar membros simultâneas de acesso aos dados, é recomendável adicionar os métodos que executa o trabalho na seção de protected ou de private da classe. Acrescentar apenas os métodos que envia ou recebe mensagens do ou do agent a seção de public da classe.
Sempre chamar o método de concurrency::agent::done para mover seu agente ao estado concluído. Você normalmente chama esse método antes que você retorne do método de run .
Próximas etapas
Para outro exemplo de um aplicativo, consulte agente- baseado Instruções passo a passo: usando join para Evitar Deadlock.
Consulte também
Tarefas
Instruções passo a passo: usando join para Evitar Deadlock
Conceitos
Biblioteca de Agentes Assíncronos
Blocos de mensagens assíncronos