Návod: Vytvoření aplikace založené na agentovi
Toto téma popisuje, jak vytvořit základní aplikaci založenou na agentech. V tomto názorném postupu můžete vytvořit agenta, který čte data z textového souboru asynchronně. Aplikace používá algoritmus kontrolního součtu Adler-32 k výpočtu kontrolního součtu obsahu tohoto souboru.
Požadavky
Abyste mohli tento návod dokončit, musíte porozumět následujícím tématům:
Oddíly
Tento názorný postup ukazuje, jak provádět následující úlohy:
Vytvoření konzolové aplikace
Tato část ukazuje, jak vytvořit konzolovou aplikaci jazyka C++, která odkazuje na soubory hlaviček, které bude program používat. Počáteční kroky se liší v závislosti na tom, jakou verzi sady Visual Studio používáte. Pokud chcete zobrazit dokumentaci pro upřednostňovanou verzi sady Visual Studio, použijte ovládací prvek selektoru verzí . Nachází se v horní části obsahu na této stránce.
Vytvoření konzolové aplikace jazyka C++ v sadě Visual Studio
V hlavní nabídce zvolte Soubor>nový>projekt a otevřete dialogové okno Vytvořit nový projekt.
V horní části dialogového okna nastavte jazyk na C++, nastavte platformu pro Windows a nastavte typ projektu na konzolu.
V filtrovaném seznamu typů projektů zvolte Konzolová aplikace a pak zvolte Další. Na další stránce zadejte
BasicAgent
jako název projektu a v případě potřeby zadejte umístění projektu.Zvolte tlačítko Vytvořit a vytvořte projekt.
Pravým tlačítkem myši klikněte na uzel projektu v Průzkumník řešení a zvolte Vlastnosti. V části Vlastnosti>konfigurace C/C++>Předkompilované hlavičky předkompilované hlavičky> zvolte Vytvořit.
Vytvoření konzolové aplikace C++ v sadě Visual Studio 2017 a starších verzích
V nabídce Soubor klepněte na tlačítko Nový a klepněte na tlačítko Projekt zobrazte dialogové okno Nový projekt.
V dialogovém okně Nový projekt vyberte uzel Visual C++ v podokně Typy projektů a pak v podokně Šablony vyberte konzolovou aplikaci Win32. Zadejte název projektu, například
BasicAgent
a klepněte na tlačítko OK zobrazte Průvodce konzolovou aplikací Win32.V dialogovém okně Průvodce konzolovou aplikací Win32 klepněte na tlačítko Dokončit.
Aktualizace souboru záhlaví
Do souboru pch.h (stdafx.h v sadě Visual Studio 2017 a starší) přidejte následující kód:
#include <agents.h>
#include <string>
#include <iostream>
#include <algorithm>
Agenti souborů hlaviček.h obsahují funkce třídy concurrency::agent .
Ověření aplikace
Nakonec ověřte, že se aplikace úspěšně vytvořila, a to tak, že ji sestavíte a spustíte. Aplikaci sestavíte tak, že v nabídce Sestavení kliknete na Sestavit řešení. Pokud se aplikace úspěšně sestaví, spusťte aplikaci kliknutím na Spustit ladění v nabídce Ladění .
[Nahoře]
Vytvoření třídy file_reader
Tato část ukazuje, jak vytvořit file_reader
třídu. Modul runtime naplánuje, aby každý agent prováděl práci v vlastním kontextu. Proto můžete vytvořit agenta, který provádí synchronně práci, ale interaguje s ostatními komponentami asynchronně. Třída file_reader
čte data z daného vstupního souboru a odesílá data z daného souboru do dané cílové komponenty.
Vytvoření třídy file_reader
Přidejte do projektu nový soubor hlaviček jazyka C++. Uděláte to tak, že v Průzkumník řešení kliknete pravým tlačítkem myši na uzel Soubory záhlaví, kliknete na Přidat a potom kliknete na Tlačítko Nová položka. V podokně Šablony vyberte Soubor záhlaví (.h). V dialogovém okně Přidat novou položku zadejte
file_reader.h
do pole Název a klepněte na tlačítko Přidat.Do file_reader.h přidejte následující kód.
#pragma once
V file_reader.h vytvořte třídu s názvem
file_reader
, která je odvozena odagent
.class file_reader : public concurrency::agent { public: protected: private: };
Do oddílu předmětu
private
přidejte následující datové členy.std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
Člen
_file_name
je název souboru, ze kterého agent čte. Člen_target
je souběžnost::ITarget objekt, do kterého agent zapíše obsah souboru. Člen_error
obsahuje všechny chyby, ke kterým dochází během životnosti agenta.Do části
file_reader
třídy přidejte následující kód profile_reader
konstruktorypublic
.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) { }
Každé přetížení konstruktoru
file_reader
nastaví datové členy. Druhé a třetí přetížení konstruktoru umožňuje vaší aplikaci používat konkrétní plánovač s vaším agentem. První přetížení používá výchozí plánovač s vaším agentem.Přidejte metodu
get_error
do veřejné částifile_reader
třídy.bool get_error(std::exception& e) { return try_receive(_error, e); }
Metoda
get_error
načte všechny chyby, ke kterým dochází během životnosti agenta.Implementujte metodu concurrency::agent::run v
protected
části třídy.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(); }
Metoda run
otevře soubor a načte z něj data. Metoda run
používá zpracování výjimek k zachycení všech chyb, ke kterým dochází během zpracování souboru.
Pokaždé, když tato metoda načte data ze souboru, volá funkci concurrency::asend , která tato data odešle do cílové vyrovnávací paměti. Odešle prázdný řetězec do cílové vyrovnávací paměti, aby indikovala konec zpracování.
Následující příklad ukazuje úplný obsah 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;
};
[Nahoře]
Použití třídy file_reader v aplikaci
Tato část ukazuje, jak pomocí file_reader
třídy číst obsah textového souboru. Ukazuje také, jak vytvořit objekt concurrency::call , který přijímá data tohoto souboru a vypočítá jeho kontrolní součet Adler-32.
Použití třídy file_reader v aplikaci
Do BasicAgent.cpp přidejte následující
#include
příkaz.#include "file_reader.h"
Do BasicAgent.cpp přidejte následující
using
direktivy.using namespace concurrency; using namespace std;
_tmain
Ve funkci vytvořte objekt concurrency::event, který signalizuje ukončení zpracování.event e;
Vytvořte
call
objekt, který aktualizuje kontrolní součet při příjmu dat.// 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; }); });
Tento
call
objekt také nastavíevent
objekt, když obdrží prázdný řetězec, aby signalizoval konec zpracování.Vytvořte
file_reader
objekt, který čte ze souboru test.txt a zapíše obsah tohoto souboru do objektucall
.file_reader reader("test.txt", calculate_checksum);
Spusťte agenta a počkejte, až se dokončí.
reader.start(); agent::wait(&reader);
Počkejte,
call
až objekt přijme všechna data a dokončí se.e.wait();
Zkontrolujte chyby v čtečce souborů. Pokud nedošlo k žádné chybě, vypočítejte konečný součet Adler-32 a vytiskněte součet do konzoly.
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; }
Následující příklad ukazuje úplný soubor BasicAgent.cpp.
// 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;
}
}
[Nahoře]
Ukázkový vstup
Toto je ukázkový obsah vstupního souboru text.txt:
The quick brown fox
jumps
over the lazy dog
Ukázkový výstup
Při použití s ukázkovým vstupem vytvoří tento program následující výstup:
Adler-32 sum is fefb0d75
Robustní programování
Pokud chcete zabránit souběžnému přístupu k datovým členům, doporučujeme přidat metody, které provádějí práci do protected
private
oddílu třídy. Do oddílu třídy přidejte metody, které odesílají nebo přijímají zprávy do nebo z agenta public
.
Vždy volejte metodu concurrency::agent::d one a přesuňte agenta do dokončeného stavu. Tuto metodu obvykle voláte před návratem run
z metody.
Další kroky
Další příklad aplikace založené na agentech naleznete v části Návod: Použití spojení k zabránění vzájemnému zablokování.
Viz také
Knihovna asynchronních agentů
Asynchronní bloky zpráv
Funkce pro předávání zpráv
Synchronizační datové struktury
Návod: Použití metody join k zabránění vzájemnému zablokování