Tutorial: Crear una aplicación basada en agente
En este tema se describe cómo crear una aplicación basada en agente básica.En este tutorial, puede crear un agente que lee datos de un archivo de texto de forma asincrónica.La aplicación utiliza el algoritmo de suma de comprobación Adler-32 para calcular la suma de comprobación del contenido de ese archivo.
Requisitos previos
Para completar este tutorial, debe comprender los siguientes temas:
Secciones
En este tutorial se muestra cómo realizar las tareas siguientes:
Crear la aplicación de consola
Crear la clase file_reader
Usar la clase file_reader en la aplicación
Crear la aplicación de consola
En esta sección se muestra cómo crear una aplicación de consola de Visual C++ que hace referencia a los archivos de encabezado que el programa usará.
Para crear una aplicación Visual C++ utilizando el Asistente para aplicación de consola Win32
En el menú Archivo, haga clic en Nuevo y, a continuación, haga clic en Proyecto para abrir el cuadro de diálogo Nuevo proyecto.
En el cuadro de diálogo Nuevo proyecto, en el panel Tipos de proyecto, seleccione el nodo Visual C++ y, a continuación, seleccione Aplicación de consola Win32 en el panel Plantillas.Escriba un nombre para el proyecto, por ejemplo, BasicAgent y, a continuación, haga clic en Aceptar para mostrar el Asistente para aplicación de consola Win32.
En el cuadro de diálogo Asistente para aplicación de consola Win32, haga clic en Finalizar.
En stdafx.h, agregue el código siguiente.
#include <agents.h> #include <string> #include <iostream> #include <algorithm>
El agents.h de archivo de encabezado contiene la funcionalidad de la concurrency::agent clase.
Compruebe que la aplicación se creó correctamente; para ello, compílela y ejecútela.Para compilar la aplicación, en el menú Generar, haga clic en Generar solución.Si la aplicación se compila correctamente, haga clic en Iniciar depuración en el menú Depurar para ejecutarla.
Top
Crear la clase file_reader
En esta sección se muestra cómo crear la clase file_reader.El runtime programa cada agente para realizar el trabajo en su propio contexto.Por tanto, puede crear un agente que realice el trabajo de forma sincrónica pero que interactúe con otros componentes de forma asincrónica.La clase file_reader lee los datos de un archivo de entrada determinado y los envía desde este archivo a un componente de destino determinado.
Para crear la clase file_reader
Agregue un nuevo archivo de encabezado de C++ al proyecto.Para ello, en el Explorador de soluciones, haga clic con el botón secundario en el nodo Archivos de encabezado, haga clic en Agregar y, a continuación, haga clic en Nuevo elemento.En el panel Plantillas, seleccione Archivo de encabezado (.h).En el cuadro de diálogo Agregar nuevo elemento, escriba file_reader.h en el cuadro Nombre y haga clic en Agregar.
En file_reader.h, agregue el siguiente código.
#pragma once
En file_reader.h, cree una clase de nombre file_reader que derive de agent.
class file_reader : public concurrency::agent { public: protected: private: };
Agregue los siguientes miembros de datos a la sección private de la clase.
std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
El miembro _file_name es el nombre de archivo del que lee el agente.El _target miembro es un concurrency::ITarget que el agente escribe el contenido del archivo de objeto.El miembro _error contiene cualquier error que se produce durante la vida del agente.
Agregue el código siguiente para los constructores de file_reader en la sección public de la clase file_reader.
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) { }
Cada sobrecarga de constructor establece los miembros de datos de file_reader.La segunda y tercera sobrecarga de constructor permiten que la aplicación use un programador concreto con el agente.La primera sobrecarga utiliza el programador predeterminado con el agente.
Agregue el método get_error a la sección pública de la clase file_reader.
bool get_error(std::exception& e) { return try_receive(_error, e); }
El método get_error recupera cualquier error que se produce durante la vida del agente.
Implementar la concurrency::agent::run método en el protected sección de la clase.
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(); }
El método run abre el archivo y lee sus datos.El método run utiliza el control de excepciones para capturar los errores que se produzcan durante el procesamiento del archivo.
Cada vez que este método lee datos desde el archivo, llama a la concurrency::asend función para enviar los datos en el búfer de destino.Envía la cadena vacía a su búfer de destino para indicar el fin del procesamiento.
En el ejemplo siguiente se muestra el contenido completo 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;
};
Top
Usar la clase file_reader en la aplicación
En esta sección se muestra cómo utilizar la clase file_reader para leer el contenido de un archivo de texto.También se muestra cómo crear un concurrency::call objeto que recibe los datos de este archivo y calcula su suma de comprobación Adler-32.
Para utilizar la clase file_reader en la aplicación
En BasicAgent.cpp, agregue la siguiente instrucción #include.
#include "file_reader.h"
En BasicAgent.cpp, agregue las siguientes directivas using.
using namespace concurrency; using namespace std;
En el _tmain de función, crear un concurrency::event objeto que señala el final de procesamiento.
event e;
Cree un objeto call que actualice la suma de comprobación cuando reciba datos.
// 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; }); });
Este objeto call también establece el objeto event cuando recibe la cadena vacía para señalar el fin del procesamiento.
Cree un objeto file_reader que lea del archivo test.txt y escriba el contenido de este archivo en el objeto call.
file_reader reader("test.txt", calculate_checksum);
Inicie el agente y espere a que finalice.
reader.start(); agent::wait(&reader);
Espere a que el objeto call reciba todos los datos y finalice.
e.wait();
Compruebe los errores del lector del archivo.Si no se ha producido ningún error, calcule la suma Adler-32 final e imprímala en la consola.
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; }
En el ejemplo siguiente se muestra el archivo BasicAgent.cpp completo.
// BasicAgent.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#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;
}
}
Top
Entrada de ejemplo
Este es el contenido de ejemplo del archivo de entrada text.txt:
The quick brown fox
jumps
over the lazy dog
Resultados del ejemplo
Cuando se utiliza con la entrada de ejemplo, este programa produce el siguiente resultado:
Adler-32 sum is fefb0d75
Programación sólida
Para evitar el acceso simultáneo a los miembros de datos, se recomienda agregar métodos que realicen el trabajo en las secciones protected o private de la clase.Agregue únicamente los métodos que envían o reciben mensajes del agente en la sección public de la clase.
Llame siempre a la concurrency::agent:: hecho método para mover el agente para el estado completado.Por lo general se llama a este método antes de volver del método run.
Pasos siguientes
Para obtener otro ejemplo de una aplicación basada en agente, vea Tutorial: Usar la clase join para evitar un interbloqueo.
Vea también
Tareas
Tutorial: Usar la clase join para evitar un interbloqueo
Conceptos
Biblioteca de agentes asincrónicos