Procédure pas à pas : création d’une application basée sur un agent
Cette rubrique explique comment créer une application basée sur un agent de base. Dans cette procédure pas à pas, vous pouvez créer un agent qui lit les données à partir d’un fichier texte de manière asynchrone. L’application utilise l’algorithme adler-32 case activée sum pour calculer l’case activée um du contenu de ce fichier.
Prérequis
Vous devez comprendre les rubriques suivantes pour effectuer cette procédure pas à pas :
Sections
Cette procédure pas à pas montre comment effectuer les tâches suivantes :
Créer l'application console
Cette section montre comment créer une application console C++ qui référence les fichiers d’en-tête que le programme utilisera. Les étapes initiales varient selon la version de Visual Studio que vous utilisez. Pour consulter la documentation sur votre version préférée de Visual Studio, utilisez le contrôle de sélection de Version . Il se trouve en haut de la table des matières de cette page.
Pour créer une application console C++ dans Visual Studio
Dans le menu principal, choisissez Fichier>Nouveau>Projet pour ouvrir la boîte de dialogue Créer un projet.
En haut de la boîte de dialogue, définissez Langage sur C++, Plateforme sur Windows et Type de projet sur Console.
À partir de la liste des types de projets, choisissez Application console, puis choisissez Suivant. Dans la page suivante, entrez
BasicAgent
le nom du projet et spécifiez l’emplacement du projet si vous le souhaitez.Choisissez le bouton Créer pour créer le projet.
Cliquez avec le bouton droit sur le nœud du projet dans Explorateur de solutions, puis choisissez Propriétés. Sous Propriétés>de configuration C/C++>En-têtes> précompilés, choisissez Créer.
Pour créer une application console C++ dans Visual Studio 2017 et versions antérieures
Dans le menu Fichier , cliquez sur Nouveau, puis sur Projet pour afficher la boîte de dialogue Nouveau projet .
Dans la boîte de dialogue Nouveau projet , sélectionnez le nœud Visual C++ dans le volet Types de projets, puis sélectionnez Application console Win32 dans le volet Modèles . Tapez un nom pour le projet, par exemple,
BasicAgent
puis cliquez sur OK pour afficher l’Assistant Application console Win32.Dans la boîte de dialogue Assistant Application console Win32, cliquez sur Terminer.
Mettre à jour le fichier d’en-tête
Dans le fichier pch.h (stdafx.h dans Visual Studio 2017 et versions antérieures), ajoutez le code suivant :
#include <agents.h>
#include <string>
#include <iostream>
#include <algorithm>
Le fichier d’en-tête agents.h contient les fonctionnalités de la classe concurrency ::agent .
Vérifier l’application
Enfin, vérifiez que l’application a été créée avec succès en créant et en l’exécutant. Pour générer l’application, dans le menu Générer , cliquez sur Générer la solution. Si l’application est générée avec succès, exécutez l’application en cliquant sur Démarrer le débogage dans le menu Débogage .
[Haut]
Création de la classe file_reader
Cette section montre comment créer la file_reader
classe. Le runtime planifie chaque agent pour effectuer le travail dans son propre contexte. Par conséquent, vous pouvez créer un agent qui fonctionne de façon synchrone, mais interagit avec d’autres composants de manière asynchrone. La file_reader
classe lit les données d’un fichier d’entrée donné et envoie des données de ce fichier à un composant cible donné.
Pour créer la classe file_reader
Ajoutez un nouveau fichier d’en-tête C++ à votre projet. Pour ce faire, cliquez avec le bouton droit sur le nœud Fichiers d’en-tête dans Explorateur de solutions, cliquez sur Ajouter, puis sur Nouvel élément. Dans le volet Modèles , sélectionnez Fichier d’en-tête (.h). Dans la boîte de dialogue Ajouter un nouvel élément , tapez
file_reader.h
la zone Nom , puis cliquez sur Ajouter.Dans file_reader.h, ajoutez le code suivant.
#pragma once
Dans file_reader.h, créez une classe nommée
file_reader
qui dérive deagent
.class file_reader : public concurrency::agent { public: protected: private: };
Ajoutez les membres de données suivants à la
private
section de votre classe.std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
Le
_file_name
membre est le nom de fichier à partir duquel l’agent lit. Le_target
membre est un objet concurrency ::ITarget dans lequel l’agent écrit le contenu du fichier. Le_error
membre contient toute erreur qui se produit pendant la durée de vie de l’agent.Ajoutez le code suivant pour les
file_reader
constructeurs à lapublic
section de lafile_reader
classe.explicit file_reader(const std::string& file_name, concurrency::ITarget<std::string>& target) : _file_name(file_name) , _target(target) { } explicit file_reader(const std::string& file_name, concurrency::ITarget<std::string>& target, concurrency::Scheduler& scheduler) : agent(scheduler) , _file_name(file_name) , _target(target) { } explicit file_reader(const std::string& file_name, concurrency::ITarget<std::string>& target, concurrency::ScheduleGroup& group) : agent(group) , _file_name(file_name) , _target(target) { }
Chaque surcharge de constructeur définit les membres de
file_reader
données. La deuxième et la troisième surcharge du constructeur permettent à votre application d’utiliser un planificateur spécifique avec votre agent. La première surcharge utilise le planificateur par défaut avec votre agent.Ajoutez la
get_error
méthode à la section publique de lafile_reader
classe.bool get_error(std::exception& e) { return try_receive(_error, e); }
La
get_error
méthode récupère toute erreur qui se produit pendant la durée de vie de l’agent.Implémentez la méthode concurrency ::agent ::run dans la
protected
section de votre classe.void run() { FILE* stream; try { // Open the file. if (fopen_s(&stream, _file_name.c_str(), "r") != 0) { // Throw an exception if an error occurs. throw std::exception("Failed to open input file."); } // Create a buffer to hold file data. char buf[1024]; // Set the buffer size. setvbuf(stream, buf, _IOFBF, sizeof buf); // Read the contents of the file and send the contents // to the target. while (fgets(buf, sizeof buf, stream)) { asend(_target, std::string(buf)); } // Send the empty string to the target to indicate the end of processing. asend(_target, std::string("")); // Close the file. fclose(stream); } catch (const std::exception& e) { // Send the empty string to the target to indicate the end of processing. asend(_target, std::string("")); // Write the exception to the error buffer. send(_error, e); } // Set the status of the agent to agent_done. done(); }
La run
méthode ouvre le fichier et lit les données à partir de celui-ci. La méthode utilise la run
gestion des exceptions pour capturer les erreurs qui se produisent pendant le traitement des fichiers.
Chaque fois que cette méthode lit les données du fichier, elle appelle la fonction concurrency ::asend pour envoyer ces données à la mémoire tampon cible. Il envoie la chaîne vide à sa mémoire tampon cible pour indiquer la fin du traitement.
L’exemple suivant montre le contenu complet de file_reader.h.
#pragma once
class file_reader : public concurrency::agent
{
public:
explicit file_reader(const std::string& file_name,
concurrency::ITarget<std::string>& target)
: _file_name(file_name)
, _target(target)
{
}
explicit file_reader(const std::string& file_name,
concurrency::ITarget<std::string>& target,
concurrency::Scheduler& scheduler)
: agent(scheduler)
, _file_name(file_name)
, _target(target)
{
}
explicit file_reader(const std::string& file_name,
concurrency::ITarget<std::string>& target,
concurrency::ScheduleGroup& group)
: agent(group)
, _file_name(file_name)
, _target(target)
{
}
// Retrieves any error that occurs during the life of the agent.
bool get_error(std::exception& e)
{
return try_receive(_error, e);
}
protected:
void run()
{
FILE* stream;
try
{
// Open the file.
if (fopen_s(&stream, _file_name.c_str(), "r") != 0)
{
// Throw an exception if an error occurs.
throw std::exception("Failed to open input file.");
}
// Create a buffer to hold file data.
char buf[1024];
// Set the buffer size.
setvbuf(stream, buf, _IOFBF, sizeof buf);
// Read the contents of the file and send the contents
// to the target.
while (fgets(buf, sizeof buf, stream))
{
asend(_target, std::string(buf));
}
// Send the empty string to the target to indicate the end of processing.
asend(_target, std::string(""));
// Close the file.
fclose(stream);
}
catch (const std::exception& e)
{
// Send the empty string to the target to indicate the end of processing.
asend(_target, std::string(""));
// Write the exception to the error buffer.
send(_error, e);
}
// Set the status of the agent to agent_done.
done();
}
private:
std::string _file_name;
concurrency::ITarget<std::string>& _target;
concurrency::overwrite_buffer<std::exception> _error;
};
[Haut]
Utilisation de la classe file_reader dans l’application
Cette section montre comment utiliser la file_reader
classe pour lire le contenu d’un fichier texte. Il montre également comment créer un objet concurrency ::call qui reçoit ces données de fichier et calcule son case activée sum Adler-32.
Pour utiliser la classe file_reader dans votre application
Dans BasicAgent.cpp, ajoutez l’instruction suivante
#include
.#include "file_reader.h"
Dans BasicAgent.cpp, ajoutez les directives suivantes
using
.using namespace concurrency; using namespace std;
Dans la
_tmain
fonction, créez un objet concurrency ::event qui signale la fin du traitement.event e;
Créez un
call
objet qui met à jour le case activée sum lorsqu’il reçoit des données.// The components of the Adler-32 sum. unsigned int a = 1; unsigned int b = 0; // A call object that updates the checksum when it receives data. call<string> calculate_checksum([&] (string s) { // If the input string is empty, set the event to signal // the end of processing. if (s.size() == 0) e.set(); // Perform the Adler-32 checksum algorithm. for_each(begin(s), end(s), [&] (char c) { a = (a + c) % 65521; b = (b + a) % 65521; }); });
Cet
call
objet définit également l’objetevent
lorsqu’il reçoit la chaîne vide pour signaler la fin du traitement.Créez un
file_reader
objet qui lit à partir du fichier test.txt et écrit le contenu de ce fichier dans l’objetcall
.file_reader reader("test.txt", calculate_checksum);
Démarrez l’agent et attendez qu’il se termine.
reader.start(); agent::wait(&reader);
Attendez que l’objet
call
reçoive toutes les données et se termine.e.wait();
Vérifiez que le lecteur de fichier contient des erreurs. Si aucune erreur ne s’est produite, calculez la somme finale d’Adler-32 et imprimez la somme dans la console.
std::exception error; if (reader.get_error(error)) { wcout << error.what() << endl; } else { unsigned int adler32_sum = (b << 16) | a; wcout << L"Adler-32 sum is " << hex << adler32_sum << endl; }
L’exemple suivant montre le fichier BasicAgent.cpp complet.
// BasicAgent.cpp : Defines the entry point for the console application.
//
#include "pch.h" // Use stdafx.h in Visual Studio 2017 and earlier
#include "file_reader.h"
using namespace concurrency;
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
// An event object that signals the end of processing.
event e;
// The components of the Adler-32 sum.
unsigned int a = 1;
unsigned int b = 0;
// A call object that updates the checksum when it receives data.
call<string> calculate_checksum([&] (string s) {
// If the input string is empty, set the event to signal
// the end of processing.
if (s.size() == 0)
e.set();
// Perform the Adler-32 checksum algorithm.
for_each(begin(s), end(s), [&] (char c) {
a = (a + c) % 65521;
b = (b + a) % 65521;
});
});
// Create the agent.
file_reader reader("test.txt", calculate_checksum);
// Start the agent and wait for it to complete.
reader.start();
agent::wait(&reader);
// Wait for the call object to receive all data and complete.
e.wait();
// Check the file reader for errors.
// If no error occurred, calculate the final Adler-32 sum and print it
// to the console.
std::exception error;
if (reader.get_error(error))
{
wcout << error.what() << endl;
}
else
{
unsigned int adler32_sum = (b << 16) | a;
wcout << L"Adler-32 sum is " << hex << adler32_sum << endl;
}
}
[Haut]
Exemple d’entrée
Voici l’exemple de contenu du fichier d’entrée text.txt :
The quick brown fox
jumps
over the lazy dog
Exemple de sortie
Lorsqu’il est utilisé avec l’exemple d’entrée, ce programme produit la sortie suivante :
Adler-32 sum is fefb0d75
Programmation fiable
Pour empêcher l’accès simultané aux membres de données, nous vous recommandons d’ajouter des méthodes qui effectuent un travail à la ou private
à la protected
section de votre classe. Ajoutez uniquement des méthodes qui envoient ou reçoivent des messages vers ou depuis l’agent à la public
section de votre classe.
Appelez toujours la méthode concurrency ::agent ::d one pour déplacer votre agent vers l’état terminé. Vous appelez généralement cette méthode avant de revenir de la run
méthode.
Étapes suivantes
Pour obtenir un autre exemple d’application basée sur un agent, consultez Procédure pas à pas : Utilisation de la jointure pour empêcher l’interblocage.
Voir aussi
Bibliothèque d’agents asynchrones
Blocs de messages asynchrones
Fonctions de passage de messages
Structures de données de synchronisation
Procédure pas à pas : utilisation de la classe join pour empêcher l’interblocage
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : Tout au long de l’année 2024, nous abandonnerons progressivement le mécanisme de retour d’information GitHub Issues pour le remplacer par un nouveau système de commentaires. Pour plus d’informations, consultez :Soumettre et afficher des commentaires pour