Compartilhar via


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

  1. No menu de Arquivo , clique em Novo, e clique em Projeto para exibir a caixa de diálogo de Novo Projeto .

  2. 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.

  3. Na caixa de diálogo de Assistente do aplicativo de console do Win32 , clique Concluir.

  4. 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 .

  5. 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

  1. 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.

  2. Em file_reader.h, adicione o seguinte código.

    #pragma once
    
  3. Em file_reader.h, crie uma classe chamada file_reader que deriva de agent.

    class file_reader : public concurrency::agent
    {
    public:
    protected:
    private:
    };
    
  4. 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.

  5. 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.

  6. 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.

  7. 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

  1. Em BasicAgent.cpp, adicione a seguinte instrução de #include .

    #include "file_reader.h"
    
  2. Em BasicAgent.cpp, adicione as políticas de using .

    using namespace concurrency;
    using namespace std;
    
  3. Na função de _tmain , crie um objeto de concurrency::event que sinalize o final do processamento.

    event e;
    
  4. 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.

  5. 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);
    
  6. Inicie o agente e espere-o para concluir.

    reader.start();
    agent::wait(&reader);
    
  7. Aguarde o objeto de call para receber todos os dados e para concluir-los.

    e.wait();
    
  8. 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

Funções de transmissão de mensagem

Estruturas de dados de sincronização