biblioteca de agentes asincrónicos

 

Para obtener la documentación más reciente de Visual Studio 2017 RC, consulte Documentación de Visual Studio 2017 RC.

La biblioteca de agentes asincrónicos (o simplemente biblioteca de agentes) proporciona un modelo de programación que permite mejorar la solidez del desarrollo de aplicaciones con la simultaneidad habilitada. La biblioteca de agentes es una biblioteca de plantillas de C++ que promueve un modelo de programación basado en actores y el paso de mensajes en proceso para tareas genéricas de flujo de datos y canalización. La Biblioteca de agentes se basa en los componentes de programación y administración de recursos del Runtime de simultaneidad.

La biblioteca de agentes proporciona alternativas al estado compartido ya que permite conectar componentes aislados a través de un modelo de comunicación asincrónica basado en el flujo de datos y no en el flujo de control. Flujo de datos hace referencia a una programación donde se realizan los cálculos cuando todos los datos necesarios del modelo está disponible; flujo de control hace referencia a un modelo de programación donde los cálculos se realizan en un orden predeterminado.

El modelo de programación de flujo de datos está relacionado con el concepto de pasar mensajes, donde los componentes independientes de un programa comunican entre sí enviándose mensajes.

La biblioteca de agentes consta de tres componentes: agentes asincrónicos, bloques de mensajes asincrónicos, y funciones para pasar mensajes. Los agentes mantienen el estado y utilizan bloques de mensajes así como funciones de paso de mensajes para comunicarse entre sí y con componentes externos. Las funciones de paso de mensajes permiten a los agentes enviar y recibir mensajes de los componentes externos. Los bloques de mensajes asincrónicos contienen mensajes y permiten a los agentes comunicar de manera sincronizada.

En la siguiente ilustración, se muestra cómo dos agentes utilizan bloques de mensajes y funciones de paso de mensajes para comunicar. En esta ilustración, agent1 envía un mensaje a agent2 utilizando el Concurrency:: Send (función) y un Concurrency:: unbounded_buffer objeto. agent2 usa el Concurrency:: Receive función para leer el mensaje. agent2 utiliza el mismo método para enviar un mensaje a agent1. Las flechas de líneas discontinuas representan el flujo de datos entre los agentes. Las flechas de líneas continuas conectan los agentes con los bloques de mensajes en los que escriben o que leen.

Los componentes de la Biblioteca de agentes

Más adelante en este tema figura un ejemplo de código en el que se implementa esta ilustración.

El modelo de programación basado en agentes ofrece varias ventajas con respecto a otros mecanismos de simultaneidad y sincronización, como eventos. Una de las ventajas consiste en que, al usar el paso de mensajes para transmitir los cambios de estado de un objeto a otro, se puede aislar el acceso a los recursos compartidos y, de este modo, mejorar la escalabilidad. El paso de mensajes ofrece como ventaja que vincula la sincronización a los datos en lugar de vincularla a un objeto de sincronización externo. De este modo, se simplifica la transmisión de datos entre los componentes y se pueden eliminar los errores de programación de las aplicaciones.

Utilice la Biblioteca de agentes en el caso de varias operaciones que deben comunicar entre sí de forma asincrónica. Los bloques de mensajes y las funciones de paso de mensajes permiten escribir aplicaciones paralelas sin necesidad de mecanismos de sincronización, como bloqueos. De este modo, el usuario podrá centrarse en la lógica de la aplicación.

El modelo de programación del agente se utiliza a menudo para crear canalizaciones de datos o redes. Una canalización de datos es una serie de componentes, cada uno de los cuales realiza una tarea concreta que contribuye a lograr un objetivo mayor. Cada componente de una canalización de flujo de datos realiza un determinado trabajo cuando recibe un mensaje de otro componente. El resultado de ese trabajo se pasa a otros componentes de la canalización o red. Los componentes pueden utilizar más funcionalidad de simultaneidad específica de otras bibliotecas, por ejemplo, el Parallel Patterns Library (PPL).

En el siguiente ejemplo, se implementa la ilustración que se muestra anteriormente en este tema.

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

Este ejemplo produce el siguiente resultado:

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

En los siguientes temas, se describe la funcionalidad que se usa en este ejemplo.

Agentes asincrónicos
Se describe el rol de los agentes asincrónicos a la hora de resolver tareas de computación mayores.

Bloques de mensajes asincrónicos
Se describen los diversos tipos de bloques de mensajes proporcionados por la Biblioteca de agentes.

Funciones que pasan mensajes
Se describen las diversas rutinas de paso de mensajes proporcionadas por la Biblioteca de agentes.

Cómo: implementar varios modelos productor-consumidor
Se describe cómo se implementa el modelo productor-consumidor en la aplicación.

Cómo: proporcionar funciones de trabajo a las clases call y transformer
Ilustra varias maneras de proporcionar funciones de trabajo a la Concurrency:: call y Concurrency:: Transformer clases.

Cómo: usar la clase transformer en una canalización de datos
Muestra cómo utilizar el Concurrency:: Transformer clase en una canalización de datos.

Cómo: seleccionar tareas completadas
Muestra cómo utilizar el Concurrency:: choice y Concurrency:: join clases para seleccionar la primera tarea para completar un algoritmo de búsqueda.

Cómo: enviar un mensaje a intervalos regulares
Muestra cómo utilizar el Concurrency:: Timer clase para enviar un mensaje a intervalos regulares.

Cómo: usar un filtro de bloque de mensaje
Muestra cómo usar un filtro para permitir que un bloque de mensajes asincrónicos para aceptar o rechazar los mensajes.

Biblioteca de modelos paralelos (PPL)
Se describe cómo utilizar diferentes modelos paralelos, como los algoritmos paralelos, en las aplicaciones.

Runtime de simultaneidad
Se describe el Runtime de simultaneidad, que simplifica la programación en paralelo, y contiene vínculos a los temas relacionados.

Mostrar: