bibliothèque d’agents asynchrones

La bibliothèque d’agents asynchrones (ou simplement la bibliothèque d’agents) fournit un modèle de programmation qui vous permet d’augmenter la robustesse du développement d’applications compatibles avec la concurrence. La bibliothèque d’agents est une bibliothèque de modèles C++ qui promeut un modèle de programmation basé sur un acteur et un passage de message in-process pour les tâches de flux de données grossières et de canalisation. La bibliothèque d’agents s’appuie sur les composants de planification et de gestion des ressources du runtime d’accès concurrentiel.

Modèle de programmation

La bibliothèque d’agents offre 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 au lieu du flux de contrôle. Le flux de données fait référence à un modèle de programmation où les calculs sont effectués lorsque toutes les données requises sont disponibles ; le flux de contrôle fait référence à un modèle de programmation dans lequel 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 transmission de messages, durant laquelle les composants indépendants d’un programme communiquent entre eux à l’aide de messages.

La bibliothèque d’agents se compose de trois composants : agents asynchrones, blocs de messages asynchrones et fonctions de transmission de messages. Les agents conservent l’état et utilisent des blocs de messages et des fonctions de transmission de messages pour communiquer entre eux et avec des composants externes. Les fonctions de transmission de messages permettent aux agents d’envoyer et de recevoir des messages vers et depuis les composants externes. Les messages asynchrones bloquent les 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 transmission de messages pour communiquer. Dans cette illustration, agent1 envoie un message à agent2 l’aide de la fonction concurrency ::send et d’un objet concurrency ::unbounded_buffer . agent2 utilise la fonction concurrency ::receive pour lire le message. agent2 utilise la même méthode pour envoyer un message à agent1. Les flèches en pointillés représentent le flux de données entre les agents. Les flèches solides connectent les agents aux blocs de message auxquels ils écrivent ou lisent.

The components of the Agents Library.

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

Le modèle de programmation de l’agent présente plusieurs avantages par rapport à d’autres mécanismes d’accès concurrentiel et de synchronisation, par exemple des événements. L’un des avantages est qu’en utilisant le passage de messages pour transmettre des modifications d’état entre les objets, vous pouvez isoler l’accès aux ressources partagées et améliorer ainsi l’extensibilité. Un avantage pour le passage de messages est qu’il lie la synchronisation aux données au lieu de l’attacher à un objet de synchronisation externe. Cela simplifie la transmission des données entre les composants et peut éliminer les erreurs de programmation dans vos applications.

Quand utiliser la bibliothèque d’agents

Utilisez la bibliothèque Agents lorsque vous avez plusieurs opérations qui doivent communiquer entre elles de manière asynchrone. Les blocs de messages et les fonctions de passage de messages vous permettent d’écrire des applications parallèles sans nécessiter 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 de l’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, chacun effectuant une tâche spécifique qui contribue à un objectif plus large. Chaque composant d’un pipeline de flux de données fonctionne lorsqu’il reçoit un message d’un autre composant. Le résultat de ce travail est passé à d’autres composants du pipeline ou du réseau. Les composants peuvent utiliser des fonctionnalités d’accès concurrentiel plus affinées à partir d’autres bibliothèques, par exemple la bibliothèque de modèles parallèles (PPL).

Exemple

L’exemple suivant implémente l’illustration illustrée précédemment 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.

Agents asynchrones
Décrit le rôle des agents asynchrones dans la résolution de tâches informatiques plus volumineuses.

Blocs de messages asynchrones
Décrit les différents types de blocs de messages fournis par la bibliothèque Agents.

Fonctions de passage de messages
Décrit les différentes routines de passage de messages fournies par la bibliothèque d’agents.

Guide pratique pour implémenter divers modèles de producteur-consommateur
Décrit comment implémenter le modèle producteur-consommateur dans votre application.

Guide pratique pour fournir des fonctions de travail aux classes call et transformer
Illustre plusieurs façons de fournir des fonctions de travail aux classes concurrency ::call et concurrency ::transformer .

Guide pratique pour utiliser la classe transformer dans un pipeline de données
Montre comment utiliser la classe concurrency ::transformer dans un pipeline de données.

Guide pratique pour effectuer une sélection parmi les tâches terminées
Montre comment utiliser les classes concurrency ::choice et concurrency ::join pour sélectionner la première tâche pour terminer un algorithme de recherche.

Guide pratique pour envoyer un message à intervalles réguliers
Montre comment utiliser la classe concurrency ::timer pour envoyer un message à intervalle régulier.

Guide pratique pour utiliser un filtre de bloc de message
Montre comment utiliser un filtre pour permettre à un bloc de messages asynchrone d’accepter ou de rejeter des messages.

Bibliothèque de modèles parallèles
Décrit comment utiliser différents modèles parallèles, tels que des algorithmes parallèles, dans vos applications.

Concurrency Runtime
Décrit le runtime d'accès concurrentiel, qui simplifie la programmation parallèle, et contient des liens vers les rubriques connexes.