Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este tópico descreve como criar um aplicativo básico baseado em agente. Nesta explicação 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 seguintes tópicos para concluir este passo a passo:
Secções
Este passo a passo demonstra como executar as seguintes tarefas:
Criando o aplicativo de console
Esta seção mostra como criar um aplicativo de console C++ que faz referência aos arquivos de cabeçalho que o programa usará. As etapas iniciais variam dependendo de qual versão do Visual Studio você está usando. Para ver a documentação da sua versão preferida do Visual Studio, use o controlador de seleção Versão . Encontra-se na parte superior do índice desta página.
Para criar um aplicativo de console C++ no Visual Studio
No menu principal, escolha Arquivo>Novo>Projeto para abrir a caixa de diálogo Criar um Novo Projeto .
Na parte superior da caixa de diálogo, defina Language como C++, defina Platform como Windows e defina Project type como Console.
Na lista filtrada de tipos de projeto, escolha Aplicativo de console e, em seguida, escolha Avançar. Na página seguinte, digite
BasicAgentcomo o nome do projeto e especifique o local do projeto, se desejado.Escolha o botão Criar para criar o projeto.
Clique com o botão direito do mouse no nó do projeto no Gerenciador de Soluções e escolha Propriedades. Em Propriedades de configuração>C/C++>Cabeçalhos pré-compilados>Cabeçalho pré-compilado, escolha Criar.
Para criar um aplicativo de console C++ no Visual Studio 2017 e versões anteriores
No menu Arquivo , clique em Novo e, em seguida, clique em Projeto para exibir a caixa de diálogo Novo Projeto .
Na caixa de diálogo Novo Projeto, selecione o nó Visual C++ no painel Tipos de projeto e, em seguida, selecione Aplicação de Consola Win32 no painel Modelos. Digite um nome para o projeto, por exemplo, e, em seguida,
BasicAgentclique em OK para exibir o Assistente de aplicativo de console Win32.Na caixa de diálogo Win32 Console Application Wizard , clique em Concluir.
Atualizar o arquivo de cabeçalho
No arquivo pch.h (stdafx.h no Visual Studio 2017 e anteriores), 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 concurrency::agent .
Verificar o aplicativo
Finalmente, verifique se o aplicativo foi criado com êxito criando-o e executando-o. Para criar o aplicativo, no menu Compilar , clique em Compilar Solução. Se o aplicativo for compilado com êxito, execute o aplicativo clicando em Iniciar Depuração no menu Depurar .
[Topo]
Criando a classe file_reader
Esta seção mostra como criar a file_reader classe. O ambiente de execução agenda cada agente para executar o trabalho no seu próprio contexto. Portanto, você pode criar um agente que executa o trabalho de forma síncrona, mas interage com outros componentes de forma assíncrona. A file_reader classe lê dados de um determinado arquivo de entrada e envia dados desse arquivo para um determinado componente de destino.
Para criar a classe file_reader
Adicione um novo arquivo de cabeçalho C++ ao seu projeto. Para fazer isso, clique com o botão direito do mouse no nó Arquivos de Cabeçalho no Gerenciador de Soluções, clique em Adicionar e, em seguida, clique em Novo Item. No painel Modelos, selecione Arquivo de cabeçalho (.h). Na caixa de diálogo Adicionar Novo Item , digite
file_reader.hna caixa Nome e clique em Adicionar.Em file_reader.h, adicione o seguinte código.
#pragma onceEm file_reader.h, crie uma classe chamada
file_readerque deriva deagent.class file_reader : public concurrency::agent { public: protected: private: };Adicione os seguintes membros de dados à
privateseção da sua classe.std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;O
_file_namemembro é o nome do arquivo do qual o agente lê. O_targetelemento é um objeto concurrency::ITarget no qual o agente escreve o conteúdo do ficheiro. O_errormembro regista qualquer erro que ocorra durante o ciclo de vida do agente.Adicione o seguinte código para os
file_readerconstrutores para apublicseção dafile_readerclasse.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 do construtor define os membros de
file_readerdados. A sobrecarga do segundo e terceiro construtores permite que seu aplicativo use um agendador específico com seu agente. A primeira sobrecarga utiliza o agendador padrão com o teu agente.Adicione o
get_errormétodo à seção pública dafile_readerclasse.bool get_error(std::exception& e) { return try_receive(_error, e); }O
get_errormétodo recupera qualquer erro que ocorra durante a vida útil do agente.Implemente o método concurrency::agent::run na
protectedseção da sua 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 run método abre o arquivo e lê dados dele. O run método usa o tratamento de exceções para capturar quaisquer erros que ocorram durante o processamento do arquivo.
Cada vez que esse método lê dados do arquivo, ele chama a função concurrency::asend para enviar esses dados para o buffer de destino. Ele envia a cadeia de caracteres vazia para seu buffer de destino para indicar o fim 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;
};
[Topo]
Usando a classe file_reader no aplicativo
Esta seção mostra como usar a file_reader classe para ler o conteúdo de um arquivo de texto. Ele também mostra como criar um objeto concurrency::call que recebe esses dados de arquivo e calcula sua soma de verificação Adler-32.
Para usar a classe file_reader em seu aplicativo
Em BasicAgent.cpp, adicione a seguinte
#includeinstrução.#include "file_reader.h"Em BasicAgent.cpp, adicionar as seguintes
usingdiretivas.using namespace concurrency; using namespace std;_tmainNa função, crie um objeto concurrency::event que sinalize o fim do processamento.event e;Crie um
callobjeto que atualize a soma de verificação quando receber 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; }); });Este
callobjeto também define oeventobjeto quando recebe a cadeia de caracteres vazia para sinalizar o fim do processamento.Crie um
file_readerobjeto que lê a partir do arquivo test.txt e grava o conteúdo desse arquivo nocallobjeto.file_reader reader("test.txt", calculate_checksum);Inicie o agente e aguarde até que ele termine.
reader.start(); agent::wait(&reader);Aguarde até que o
callobjeto receba todos os dados e termine.e.wait();Verifique se há erros no leitor de arquivos. Se não tiver ocorrido nenhum erro, calcule a soma final do Adler-32 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 BasicAgent.cpp completo.
// 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;
}
}
[Topo]
Entrada de Exemplo
Este é o conteúdo de exemplo do arquivo de entrada text.txt:
The quick brown fox
jumps
over the lazy dog
Saída de amostra
Quando usado com a entrada de amostra, este programa produz a seguinte saída:
Adler-32 sum is fefb0d75
Programação robusta
Para evitar o acesso simultâneo a membros de dados, recomendamos adicionar métodos que executem trabalho à protected ou à private seção da sua classe. Adicione apenas métodos que enviem ou recebam mensagens de ou para o agente na public seção da sua classe.
Utilize o método concurrency::agent::done para mover o seu agente para o estado concluído. Normalmente, você chama esse método antes de retornar do run método.
Próximas Etapas
Para obter outro exemplo de uma aplicação baseada em agente, consulte Guia passo a passo: Usando a junção para evitar o deadlock.
Ver também
Biblioteca de agentes assíncronos
Blocos de mensagens assíncronas
Funções de passagem de mensagens
Estruturas de dados de sincronização
Passo a passo: Usando 'join' para prevenir deadlock