Bibliothèque d'agents asynchrones

La Bibliothèque d'agents asynchrones (ou simplement Bibliothèque d'agents) fournit un modèle de programmation qui vous permet d'accroître la robustesse du développement d'applications à accès concurrentiel.La bibliothèque d'agents est une bibliothèque de modèles C++ qui encourage un modèle de programmation basé sur acteur et un passage de messages in-process pour des tâches de traitement « pipeline » et de flux de données de granularité grossière.La Bibliothèque d'agents repose sur les composants de gestion des ressources et de planification du runtime d'accès concurrentiel.

Modèle de programmation

La bibliothèque d'agents fournit des alternatives à l'état partagé en vous permettant de connecter des composants isolés via un modèle de communication asynchrone basé sur le flux de données plutôt que le flux de contrôle.Le terme flux de données fait référence à un modèle de programmation où les calculs sont effectués lorsque toutes les données obligatoires sont disponibles ; le terme flux de contrôle fait référence à un modèle de programmation où les calculs sont effectués dans un ordre prédéterminé.

Le modèle de programmation de flux de données est lié au concept de passage de message, où les composants indépendants d'un programme communiquent les uns avec les autres en envoyant des messages.

La Bibliothèque d'agents est constituée de trois composants : les agents asynchrones, les blocs de messages asynchrones et les fonctions de passage de messages.Les agents maintiennent l'état et utilisent des blocs de messages et des fonctions de passage de messages pour communiquer les uns avec les autres et avec les composants externes.Les fonctions de passage de messages permettent aux agents d'envoyer et de recevoir des messages vers et en provenance des composants externes.Les blocs de messages asynchrones stockent des messages et permettent aux agents de communiquer de manière synchronisée.

L'illustration suivante montre comment deux agents utilisent des blocs de messages et des fonctions de passage de messages pour communiquer.Dans cette illustration, agent1 envoie un message à agent2 à l'aide de la concurrency::send fonction et un concurrency::unbounded_buffer objet.agent2utilise le concurrency::receive fonction pour lire le message.agent2 utilise la même méthode pour envoyer un message à agent1.Les flèches pointillées représentent le flux de données entre les agents.Les flèches solides connectent les agents aux blocs de messages dans lesquels ils écrivent ou à partir desquels ils lisent.

Composants de la bibliothèque des agents

Un exemple de code qui implémente cette illustration est fourni plus loin dans cette rubrique.

Le modèle de programmation d'agent présente plusieurs avantages par rapport aux autres mécanismes de synchronisation et d'accès concurrentiel, par exemple les événements.L'un des avantages offerts est qu'en utilisant le passage de message pour transmettre des modifications d'état entre des objets, vous pouvez isoler l'accès aux ressources partagées et par conséquent améliorer l'extensibilité.Le passage de message offre l'avantage de lier la synchronisation aux données plutôt qu'à un objet de synchronisation externe.Cela simplifie la transmission des données parmi les composants et peut éliminer les erreurs de programmation dans vos applications.

Quand utiliser la bibliothèque d'agents ?

Utilisez la Bibliothèque d'agents lorsque vous avez plusieurs opérations qui doivent communiquer l'une avec l'autre de façon asynchrone.Les blocs de messages et fonctions de passage de messages vous permettent d'écrire des applications parallèles sans avoir de besoin de mécanismes de synchronisation tels que des verrous.Cela vous permet de vous concentrer sur la logique d'application.

Le modèle de programmation d'agent est souvent utilisé pour créer des pipelines de données ou des réseaux.Un pipeline de données est une série de composants qui effectuent chacun une tâche spécifique qui contribue à un plus grand objectif.Chaque composant d'un pipeline de flux de données effectue un travail lorsqu'il reçoit un message d'un autre composant.Le résultat de ce travail est passé à d'autres composants dans le pipeline ou réseau.Les composants peuvent utiliser des fonctionnalités d'accès concurrentiel plus affinées d'autres bibliothèques, par exemple, la Bibliothèque de modèles parallèles.

Exemple

L'exemple suivant implémente l'illustration fournie plus haut dans cette rubrique.

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

Cet exemple produit la sortie suivante :

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

Les rubriques suivantes décrivent les fonctionnalités utilisées dans cet exemple.

Rubriques connexes