Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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
BasicAgentcome 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 ,
BasicAgente 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.hnella casella Nome e quindi fare clic su Aggiungi.In file_reader.h aggiungere il codice seguente.
#pragma onceIn file_reader.h creare una classe denominata
file_readerche deriva daagent.class file_reader : public concurrency::agent { public: protected: private: };Aggiungere i membri dati seguenti alla
privatesezione della classe.std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;Il
_file_namemembro è il nome di file da cui l'agente legge. Il_targetmembro è un oggetto concurrency::ITarget in cui l'agente scrive il contenuto del file. Il_errormembro contiene qualsiasi errore che si verifica durante la durata dell'agente.Aggiungere il codice seguente per i
file_readercostruttori allapublicsezione dellafile_readerclasse .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_readermembri 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_errormetodo alla sezione pubblica dellafile_readerclasse .bool get_error(std::exception& e) { return try_receive(_error, e); }Il
get_errormetodo recupera qualsiasi errore che si verifica durante la durata dell'agente.Implementare il metodo concurrency::agent::run nella
protectedsezione 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;_tmainNella funzione creare un oggetto concurrency::event che segnala la fine dell'elaborazione.event e;Creare un
calloggetto 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
calloggetto imposta anche l'oggettoeventquando riceve la stringa vuota per segnalare la fine dell'elaborazione.Creare un
file_readeroggetto 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
callriceva 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