Procedura dettagliata: Creazione di un'applicazione basata sugli agenti
Questo argomento descrive come creare un'applicazione basata su agente di base. In questa procedura dettagliata è possibile creare un agente che legge i dati da un file di testo in modo asincrono. L'applicazione utilizza l'algoritmo di checksum Di Adler-32 per calcolare il checksum del contenuto del file.
Prerequisiti
Per completare questa procedura dettagliata, è necessario comprendere gli argomenti seguenti:
Sezioni
Questa procedura dettagliata illustra come eseguire le attività seguenti:
Creazione dell'applicazione console
Questa sezione illustra come creare un'applicazione console C++ che fa riferimento ai file di intestazione che verranno usati dal programma. I passaggi iniziali variano a seconda della versione di Visual Studio in uso. Per visualizzare la documentazione relativa alla versione preferita di Visual Studio, usare il controllo selettore della versione . Si trova nella parte superiore del sommario in questa pagina.
Per creare un'applicazione console C++ in Visual Studio
Dal menu principale scegliere File>Nuovo>Progetto per aprire la finestra di dialogo Crea nuovo progetto.
Nella parte superiore della finestra di dialogo impostare Linguaggio su C++ , impostare Piattaforma su Windows e impostare Tipo di progetto su Console.
Nell'elenco filtrato dei tipi di progetto scegliere App console e quindi scegliere Avanti. Nella pagina successiva immettere
BasicAgent
come nome per il progetto e specificare il percorso del progetto, se necessario.Scegliere il pulsante Crea per creare il progetto.
Fare clic con il pulsante destro del mouse sul nodo del progetto in Esplora soluzioni e scegliere Proprietà. In Proprietà>di configurazione C/C++>Intestazioni precompilate intestazioni>precompilate scegliere Crea.
Per creare un'applicazione console C++ in Visual Studio 2017 e versioni precedenti
Scegliere Nuovo dal menu File e quindi fare clic su Progetto per visualizzare la finestra di dialogo Nuovo progetto.
Nella finestra di dialogo Nuovo progetto selezionare il nodo Visual C++ nel riquadro Tipi di progetto e quindi selezionare Applicazione console Win32 nel riquadro Modelli. Digitare un nome per il progetto, ad esempio ,
BasicAgent
e quindi fare clic su OK per visualizzare la Creazione guidata applicazione console Win32.Nella finestra di dialogo Creazione guidata applicazione console Win32 fare clic su Fine.
Aggiornare il file di intestazione
Nel file pch.h (stdafx.h in Visual Studio 2017 e versioni precedenti) aggiungere il codice seguente:
#include <agents.h>
#include <string>
#include <iostream>
#include <algorithm>
Il file di intestazione agents.h contiene la funzionalità della classe concurrency::agent .
Verificare l'applicazione
Infine, verificare che l'applicazione sia stata creata correttamente compilando ed eseguendola. Per compilare l'applicazione, scegliere Compila soluzione dal menu Compila. Se l'applicazione viene compilata correttamente, eseguire l'applicazione facendo clic su Avvia debug dal menu Debug .
Creazione della classe file_reader
Questa sezione illustra come creare la file_reader
classe . Il runtime pianifica ogni agente per eseguire operazioni nel proprio contesto. Pertanto, è possibile creare un agente che esegue il lavoro in modo sincrono, ma interagisce in modo asincrono con altri componenti. La file_reader
classe legge i dati da un determinato file di input e invia dati da tale file a un determinato componente di destinazione.
Per creare la classe file_reader
Aggiungere un nuovo file di intestazione C++ al progetto. A tale scopo, fare clic con il pulsante destro del mouse sul nodo File di intestazione in Esplora soluzioni, scegliere Aggiungi e quindi fare clic su Nuovo elemento. Nel riquadro Modelli selezionare File di intestazione (.h). Nella finestra di dialogo Aggiungi nuovo elemento digitare
file_reader.h
nella casella Nome e quindi fare clic su Aggiungi.In file_reader.h aggiungere il codice seguente.
#pragma once
In file_reader.h creare una classe denominata
file_reader
che deriva daagent
.class file_reader : public concurrency::agent { public: protected: private: };
Aggiungere i membri dati seguenti alla
private
sezione della classe.std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
Il
_file_name
membro è il nome di file da cui l'agente legge. Il_target
membro è un oggetto concurrency::ITarget in cui l'agente scrive il contenuto del file. Il_error
membro contiene qualsiasi errore che si verifica durante la durata dell'agente.Aggiungere il codice seguente per i
file_reader
costruttori allapublic
sezione dellafile_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) { }
Ogni overload del costruttore imposta i
file_reader
membri dati. Il secondo e terzo overload del costruttore consente all'applicazione di usare un'utilità di pianificazione specifica con l'agente. Il primo overload usa l'utilità di pianificazione predefinita con l'agente.Aggiungere il
get_error
metodo alla sezione pubblica dellafile_reader
classe .bool get_error(std::exception& e) { return try_receive(_error, e); }
Il
get_error
metodo recupera qualsiasi errore che si verifica durante la durata dell'agente.Implementare il metodo concurrency::agent::run nella
protected
sezione della 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(); }
Il run
metodo apre il file e legge i dati da esso. Il run
metodo usa la gestione delle eccezioni per acquisire eventuali errori che si verificano durante l'elaborazione dei file.
Ogni volta che questo metodo legge i dati dal file, chiama la funzione concurrency::asend per inviare tali dati al buffer di destinazione. Invia la stringa vuota al buffer di destinazione per indicare la fine dell'elaborazione.
L'esempio seguente mostra il contenuto completo di 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;
};
Uso della classe file_reader nell'applicazione
Questa sezione illustra come usare la file_reader
classe per leggere il contenuto di un file di testo. Viene inoltre illustrato come creare un oggetto concurrency::call che riceve questi dati di file e calcola il checksum di Adler-32.
Per usare la classe file_reader nell'applicazione
In BasicAgent.cpp aggiungere l'istruzione seguente
#include
.#include "file_reader.h"
In BasicAgent.cpp aggiungere le direttive seguenti
using
.using namespace concurrency; using namespace std;
_tmain
Nella funzione creare un oggetto concurrency::event che segnala la fine dell'elaborazione.event e;
Creare un
call
oggetto che aggiorna il checksum quando riceve i dati.// 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; }); });
Questo
call
oggetto imposta anche l'oggettoevent
quando riceve la stringa vuota per segnalare la fine dell'elaborazione.Creare un
file_reader
oggetto che legge dal file test.txt e scrive il contenuto del file nell'oggettocall
.file_reader reader("test.txt", calculate_checksum);
Avviare l'agente e attendere il completamento.
reader.start(); agent::wait(&reader);
Attendere che l'oggetto
call
riceva tutti i dati e terminare.e.wait();
Controllare la presenza di errori nel lettore di file. Se non si è verificato alcun errore, calcolare la somma finale di Wsus-32 e stampare la somma nella 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; }
Nell'esempio seguente viene illustrato il file di BasicAgent.cpp completo.
// 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;
}
}
Input di esempio
Questo è il contenuto di esempio del file di input text.txt:
The quick brown fox
jumps
over the lazy dog
Output di esempio
Se usato con l'input di esempio, questo programma genera l'output seguente:
Adler-32 sum is fefb0d75
Programmazione efficiente
Per impedire l'accesso simultaneo ai membri dati, è consigliabile aggiungere metodi che eseguono operazioni alla protected
sezione o private
della classe. Aggiungere solo metodi che inviano o ricevono messaggi da o verso l'agente alla public
sezione della classe.
Chiamare sempre il metodo concurrency::agent::d one per spostare l'agente nello stato completato. Questo metodo viene in genere chiamato prima di tornare dal run
metodo .
Passaggi successivi
Per un altro esempio di applicazione basata su agente, vedere Procedura dettagliata: Uso del join per impedire deadlock.
Vedi anche
Libreria di agenti asincroni
Blocchi dei messaggi asincroni
Funzioni di passaggio dei messaggi
Strutture di dati di sincronizzazione
Procedura dettagliata: uso della classe join per impedire un deadlock