Wskazówki: tworzenie aplikacji opartej o agentów
W tym temacie opisano sposób tworzenia podstawowych aplikacji opartych na agentach.W tym instruktażu można utworzyć agenta, który odczytuje dane z pliku tekstowego asynchronicznie.Aplikacja używa algorytmu suma kontrolna Adler-32 do wyliczenia tej sumy kontrolnej zawartość tego pliku.
Wymagania wstępne
Należy zrozumieć, do przeprowadzenia tego instruktażu następujące tematy:
Sekcje
W tym instruktażu przedstawiono sposób wykonywania następujących zadań:
Tworzenie aplikacji konsoli
Tworzenie klasy file_reader
Korzystając z klasy file_reader w aplikacji
Tworzenie aplikacji konsoli
W tej sekcji przedstawiono sposób tworzenia aplikacji konsoli Visual C++, który odwołuje się do plików nagłówek, który będzie używany w programie.
Aby utworzyć aplikację Visual C++ przy użyciu kreatora aplikacji konsoli Win32
Na pliku menu, kliknij przycisk Nowy, a następnie kliknij przycisk Projekt do wyświetlania Nowy projekt okno dialogowe.
W Nowy projekt okno dialogowe, wybierz Visual C++ węzeł w typów projektów okienka, a następnie wybierz Win32 Console Application w Szablony okienka.Wpisz nazwę projektu, na przykład, BasicAgent, a następnie kliknij przycisk OK do wyświetlania Kreatora aplikacji konsoli Win32.
W Kreatora aplikacji konsoli Win32 okno dialogowe, kliknij przycisk Zakończ.
W stdafx.h Dodaj następujący kod.
#include <agents.h> #include <string> #include <iostream> #include <algorithm>
Agents.h pliku nagłówka zawiera funkcje concurrency::agent klasy.
Sprawdź, czy aplikacja został pomyślnie utworzony przez tworzenie i uruchamianie go.Do tworzenia aplikacji, z budować menu, kliknij przycisk Budowania rozwiązania.Jeśli aplikacja tworzy pomyślnie, uruchomić aplikację, klikając Rozpocząć debugowanie na Debug menu.
[U góry]
Tworzenie klasy file_reader
W tej sekcji przedstawiono sposób tworzenia file_reader klasy.Środowisko wykonawcze planuje każdego agenta do wykonywania pracy w swoim własnym kontekście.W związku z tym można utworzyć agenta, który wykonuje pracę synchronicznie, lecz współdziała z innymi składnikami asynchronicznie.file_reader Klasy odczytuje dane z określonego pliku wejściowego i wysyła dane z tego pliku do części określonej wartości docelowej.
Aby utworzyć klasę file_reader
Dodaj nowy plik nagłówka C++ do projektu.Aby to zrobić, kliknij prawym przyciskiem myszy Pliki nagłówków węzła w Solution Explorer, kliknij Dodaj, a następnie kliknij przycisk Dodaje się ppkt.W Szablony okienka, wybierz Pliku nagłówka (.h).W Dodaj nowy element okno dialogowe, typ file_reader.h w Nazwa a następnie kliknij przycisk Dodaj.
W file_reader.h Dodaj następujący kod.
#pragma once
W file_reader.h, należy utworzyć klasę o nazwie file_reader wywodzi się z agent.
class file_reader : public concurrency::agent { public: protected: private: };
Dodaj następujące elementy danych do private część swojej klasy.
std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
_file_name Członek jest agent odczytuje z nazwą pliku._target Element członkowski jest concurrency::ITarget że agent zapisuje zawartość pliku do obiektu._error Członkowskich posiada wszelkie błędy występujące podczas użytkowania programu agent.
Dodaj następujący kod dla file_reader konstruktorów public części file_reader klasy.
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) { }
Ustawia każdego przeciążenie Konstruktor file_reader danych członków.Przeciążenie Konstruktor drugiego i trzeciego umożliwia aplikacji do korzystania z agenta określonego harmonogramu.Pierwszy przeciążenie używa Harmonogram domyślny agent.
Dodaj get_error metoda do publicznej sekcji file_reader klasy.
bool get_error(std::exception& e) { return try_receive(_error, e); }
get_error Metoda pobiera wszelkie błędy występujące podczas użytkowania programu agent.
Implementacja concurrency::agent::run metoda w protected część swojej klasy.
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(); }
run Metoda otwiera plik i odczytuje dane z niego.run Metoda wykorzystuje do przechwytywania błędów, które wystąpiły podczas przetwarzania pliku obsługi wyjątków.
Wywołuje każdorazowo ta metoda odczytuje dane z pliku concurrency::asend funkcję wysyłania danych do buforu docelowego.Wysyła ciąg pusty do buforu docelowego do wskazywania końca przetwarzania.
Poniższy przykład pokazuje Pełna zawartość 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;
};
[U góry]
Korzystając z klasy file_reader w aplikacji
W tej sekcji przedstawiono sposób użycia file_reader klasy do odczytu zawartości pliku tekstowego.Również pokazuje, jak utworzyć concurrency::call obiekt, który odbiera dane tego pliku i oblicza sumę kontrolną jego Adler-32.
Aby użyć klasy file_reader w aplikacji
Dodać następujący wpis w BasicAgent.cpp, #include instrukcji.
#include "file_reader.h"
Dodać następujący wpis w BasicAgent.cpp, using dyrektyw.
using namespace concurrency; using namespace std;
W _tmain funkcji, tworzenie concurrency::event obiekt, który sygnalizuje koniec przetwarzania.
event e;
Tworzenie call obiekt, który aktualizuje sumy kontrolnej, gdy odbiera dane.
// 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; }); });
To call obiektu ustawia również event obiektu po odebraniu pusty ciąg znaków do sygnalizowania zakończenia przetwarzania.
Tworzenie file_reader obiekt, który test.txt Plik odczytuje i zapisuje zawartość tego pliku do call obiektu.
file_reader reader("test.txt", calculate_checksum);
Uruchom agenta i poczekaj na zakończenie.
reader.start(); agent::wait(&reader);
Poczekaj, aż call obiekt, aby odebrać wszystkie dane i Zakończ.
e.wait();
Sprawdź czytnik plików błędów.Jeśli nie wystąpił żaden błąd, obliczyć Suma końcowa Adler-32 i drukowanie sum do konsoli.
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; }
Poniższy przykład pokazuje cały plik BasicAgent.cpp.
// 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;
}
}
[U góry]
Przykładowe dane wejściowe
To jest zawartość próbki text.txt pliku wejściowego:
Przykładowe dane wyjściowe
Gdy używana z przykładowe dane wejściowe, to program wyświetli:
Niezawodne programowanie
Aby zapobiec równoczesny dostęp do danych członków, firma Microsoft zaleca, aby dodać metody, które wykonują pracę przy protected lub private część swojej klasy.Metody, które wysyłają lub odbierają wiadomości z agenta lub dodawać tylko public część swojej klasy.
Wywoływanie zawsze concurrency::agent:: Sporządzono metoda przeniesienia swojego agenta do stanu wykonanego.Zazwyczaj można wywołać tej metody przed zwróceniem z run metody.
Następne kroki
Innym przykładem aplikacji opartych na agentach, zobacz Wskazówki: korzystanie ze złączy w celu zapobiegania zakleszczeniom.
Zobacz też
Zadania
Wskazówki: korzystanie ze złączy w celu zapobiegania zakleszczeniom
Koncepcje
Biblioteka agentów asynchronicznych
Bloki komunikatów asynchronicznych