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 en utilisant la fonction Concurrency::send et 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 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.
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 la montée en charge. 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
Agents asynchrones
Décrit le rôle des agents asynchrones dans la résolution de plus grandes tâches de calcul.Blocs de messages asynchrones
Décrit les différents types de blocs de messages fournis par la Bibliothèque d'agents.Fonctions de passage de messages
Décrit les différentes routines de passage de messages fournis par la Bibliothèque d'agents.Comment : implémenter divers modèles de producteur-consommateur
Explique comment implémenter le modèle de producteur-consommateur dans votre application.Comment : fournir des fonctions de travail aux classes call et transformer
Illustre plusieurs méthodes permettant de fournir des fonctions de travail aux classes Concurrency::call et Concurrency::transformer.Comment : utiliser la classe transformer dans un pipeline de données
Indique comment utiliser la classe Concurrency::transformer dans un pipeline de données.Comment : 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 afin de terminer un algorithme de recherche.Comment : envoyer un message à intervalles réguliers
Indique comment utiliser la classe Concurrency::timer pour envoyer un message à intervalle régulier.Comment : utiliser un filtre de bloc de message
Montre comment utiliser un filtre pour permettre à un bloc de message asynchrone d'accepter ou de refuser des messages.Bibliothèque de modèles parallèles
Décrit comment utiliser différents modèles parallèles, tels que les 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 des rubriques connexes.