Biblioteca de agentes assíncronos

A biblioteca de agentes assíncronos (ou apenas Biblioteca de agentes) fornece um modelo de programação que permite aumentar a robustez do desenvolvimento de aplicativo habilitado para simultaneidade. A biblioteca de agentes é uma biblioteca de modelos C++ promove um modelo de programação baseada em ator e a mensagem no processo, passando para o fluxo de dados refinado e a canalização de tarefas. A biblioteca de agentes amplia os componentes de gerenciamento de recursos e o agendamento do Runtime de simultaneidade.

Modelo de programação

A biblioteca de agentes fornece alternativas para o estado compartilhado, permitindo que você conecte componentes isolados por meio de um modelo de comunicação assíncrona baseada em fluxo de dados em vez de fluxo de controle. Fluxo de dados se refere a uma programação em que os cálculos são feitos quando todos os dados necessários do modelo está disponível. controlar o fluxo de se refere a um modelo de programação em que os cálculos são feitos em uma ordem predeterminada.

O modelo de programação de fluxo de dados está relacionado ao conceito de passagem de mensagens, onde os componentes independentes de um programa se comunicar entre si através do envio de mensagens.

A biblioteca de agentes é formada por três componentes: agentes assíncronos, blocos de mensagens assíncronas, e funções de transmissão de mensagens. Agentes mantêm o estado e usam os blocos de mensagens e funções de transmissão de mensagens se comuniquem entre si e com componentes externos. Funções de transmissão de mensagens permitem que os agentes enviar e receber mensagens de e para os componentes externos. Blocos de mensagens assíncronas mantêm as mensagens e habilitar agentes para se comunicar de maneira sincronizada.

A ilustração a seguir mostra como dois agentes de blocos de mensagem de uso e funções de transmissão de mensagens para se comunicar. Nesta ilustração, agent1 envia uma mensagem para agent2 usando o Concurrency::send função e um Concurrency::unbounded_buffer objeto. agent2usa a Concurrency::receive a função para ler a mensagem. agent2usa o mesmo método para enviar uma mensagem para agent1. Setas tracejadas representam o fluxo de dados entre os agentes. As setas sólidas conectam os agentes para os blocos de mensagem que eles gravar ou ler.

Os componentes da Biblioteca de Agentes

Um exemplo de código que implementa esta ilustração é mostrado neste tópico.

O modelo de programação do agente tem várias vantagens em relação a outros mecanismos de simultaneidade e sincronização, por exemplo, os eventos. Uma vantagem é que, usando a passagem de mensagem para transmitir as alterações de estado entre objetos, você pode isolar o acesso a recursos compartilhados e, assim, melhorar a escalabilidade. Uma vantagem da transmissão de mensagens é que ele vincula a sincronização de dados em vez de juntando-se a um objeto de sincronização externa. Isso simplifica a transmissão de dados entre componentes e pode eliminar erros de programação em seus aplicativos.

Quando usar a biblioteca de agentes

Use a biblioteca de agentes, quando você tem várias operações que precisam se comunicar com outro modo assíncrono. Blocos de mensagens e funções de transmissão de mensagens permitem que você escrever aplicativos em paralelo, sem a necessidade de mecanismos de sincronização como, por exemplo, os bloqueios. Isso permite que você se concentrar na lógica de aplicativo.

O modelo de programação do agente é freqüentemente usado para criar pipelines de dados ou redes. Um pipeline de dados é uma série de componentes, cada uma das quais executa uma tarefa específica que contribui para um objetivo maior. Cada componente em um pipeline de fluxo de dados executa o trabalho quando ele recebe uma mensagem de outro componente. O resultado desse trabalho é passado para outros componentes na rede ou de pipeline. Os componentes podem usar mais funcionalidade de simultaneidade refinada das outras bibliotecas, por exemplo, o Biblioteca paralela de padrões (PPL).

Exemplo

O exemplo a seguir implementa a ilustração mostrada anteriormente neste tópico.

// 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);
}

Esse exemplo produz a seguinte saída.

agent1: sending request...
agent2: received 'request'.
agent2: sending response...
agent1: received '42'.

Os tópicos a seguir descrevem a funcionalidade usada neste exemplo.

Tópicos relacionados