Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a témakör azt ismerteti, hogyan hozhat létre alapszintű ügynökalapú alkalmazást. Ebben az útmutatóban létrehozhat egy ügynököt, amely aszinkron módon olvas be adatokat egy szövegfájlból. Az alkalmazás az Adler-32 ellenőrzőösszeg-algoritmussal számítja ki a fájl tartalmának ellenőrzőösszegét.
Előfeltételek
Az útmutató elvégzéséhez a következő témaköröket kell ismernie:
Szakaszok
Ez az útmutató bemutatja, hogyan hajthatja végre a következő feladatokat:
A konzolalkalmazás létrehozása
Ez a szakasz bemutatja, hogyan hozhat létre olyan C++ konzolalkalmazást, amely a program által használni kívánt fejlécfájlokra hivatkozik. A kezdeti lépések a Visual Studio használt verziójától függően változnak. A Visual Studio előnyben részesített verziójának dokumentációját a Verzió választóvezérlő használatával tekintheti meg. A lap tartalomjegyzékének tetején található.
C++ konzolalkalmazás létrehozása a Visual Studióban
A főmenüBen válassza azÚj>projekt> lehetőséget az Új projekt létrehozása párbeszédpanel megnyitásához.
A párbeszédpanel tetején állítsa a Nyelvbeállítást C++-ra, állítsa a PlatformotWindowsra, és állítsa a Projekt típusátkonzolra.
A projekttípusok szűrt listájában válassza a Konzolalkalmazás lehetőséget, majd a Tovább gombot. A következő lapon adja meg
BasicAgenta projekt nevét, és szükség esetén adja meg a projekt helyét.A projekt létrehozásához válassza a Létrehozás gombot.
Kattintson a jobb gombbal a projektcsomópontra a Megoldáskezelőben, és válassza a Tulajdonságok parancsot. A Konfiguráció tulajdonságai>C/C++>Előre összeállított fejlécek>Előre összeállított fejléc válassza a Létrehozás lehetőséget.
C++ konzolalkalmazás létrehozása a Visual Studio 2017-ben és korábbi verzióiban
A FájlmenüBen kattintson az Új, majd a Project gombra az Új projekt párbeszédpanel megjelenítéséhez.
Az Új projekt párbeszédpanelen válassza a Visual C++ csomópontot a Projekttípusok panelen, majd válassza a Win32 Konzolalkalmazás lehetőséget a Sablonok panelen. Írja be például a projekt nevét,
BasicAgentmajd kattintson az OKgombra a Win32 Konzolalkalmazás varázsló megjelenítéséhez.A Win32 Konzolalkalmazás varázsló párbeszédpanelen kattintson a Befejezés gombra.
A fejlécfájl frissítése
A pch.h fájlban (stdafx.h a Visual Studio 2017-ben és korábbi verzióiban) adja hozzá a következő kódot:
#include <agents.h>
#include <string>
#include <iostream>
#include <algorithm>
A agents.h fejlécfájl a concurrency::agent osztály funkcióit tartalmazza.
Az alkalmazás ellenőrzése
Végül ellenőrizze, hogy az alkalmazás sikeresen létrejött-e az alkalmazás létrehozásával és futtatásával. Az alkalmazás létrehozásához kattintson a Build menü Build Solution (Megoldás létrehozása) parancsára. Ha az alkalmazás sikeresen buildel, futtassa az alkalmazást a Hibakeresés menü Hibakeresés indítása parancsára kattintva.
[Felső]
A file_reader osztály létrehozása
Ez a szakasz bemutatja, hogyan hozhatja létre az osztályt file_reader . A futtatókörnyezet ütemezi az egyes ügynököket, hogy a saját kontextusukban végezzenek munkát. Ezért létrehozhat olyan ügynököt, amely szinkron módon végzi a munkát, de aszinkron módon kommunikál más összetevőkkel. Az file_reader osztály beolvassa az adatokat egy adott bemeneti fájlból, és adatokat küld a fájlból egy adott célösszetevőnek.
A file_reader osztály létrehozása
Adjon hozzá egy új C++ fejlécfájlt a projekthez. Ehhez kattintson a jobb gombbal a Fejlécfájlok csomópontra a Megoldáskezelőben, kattintson a Hozzáadás, majd az Új elem parancsra. A Sablonok panelen válassza a Fejlécfájl (.h) lehetőséget. Az Új elem hozzáadása párbeszédpanelen írja be
file_reader.ha Név mezőbe, majd kattintson a Hozzáadás gombra.A file_reader.h fájlban adja hozzá a következő kódot.
#pragma onceA file_reader.h-ban hozzon létre egy osztályt
file_reader, amely a következőbőlagentszármazik: .class file_reader : public concurrency::agent { public: protected: private: };Adja hozzá az alábbi adattagokat az
privateosztály szakaszához.std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;A
_file_nametag az az ügynök által beolvasott fájlnév. A_targettag egy egyidejűség::ITarget-objektum , amelybe az ügynök a fájl tartalmát írja. A_errortag tartalmazza az ügynök működése során előforduló bármilyen hibát.Adja hozzá a
file_readerkonstruktorok következő kódját apublicosztályfile_readerszakaszához.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) { }Minden konstruktor túlterhelése beállítja az
file_readeradattagokat. A második és a harmadik konstruktor túlterhelése lehetővé teszi, hogy az alkalmazás egy adott ütemezőt használjon az ügynökével. Az első túlterhelés az alapértelmezett ütemezőt használja az ügynökkel.Adja hozzá a
get_errormetódust afile_readerosztály nyilvános szakaszához.bool get_error(std::exception& e) { return try_receive(_error, e); }A
get_errormetódus lekéri az ügynök élettartama során előforduló hibákat.Implementálja a futtatás::agent::run metódust az osztály részében.
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(); }
A run metódus megnyitja a fájlt, és adatokat olvas be belőle. A run metódus kivételkezeléssel rögzíti a fájlfeldolgozás során előforduló hibákat.
Minden alkalommal, amikor ez a metódus adatokat olvas be a fájlból, meghívja a concurrency::asend függvényt, hogy elküldje az adatokat a célpufferbe. Elküldi az üres sztringet a célpufferbe, hogy jelezze a feldolgozás végét.
Az alábbi példa a file_reader.h teljes tartalmát mutatja be.
#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;
};
[Felső]
A file_reader osztály használata az alkalmazásban
Ez a szakasz bemutatja, hogyan használható az file_reader osztály egy szövegfájl tartalmának beolvasására. Azt is bemutatja, hogyan hozható létre egy concurrency::call objektum, amely fogadja a fájladatokat és kiszámítja az Adler-32 ellenőrzőösszeget.
A file_reader osztály használata az alkalmazásban
A BasicAgent.cpp adja hozzá a következő
#includeutasítást.#include "file_reader.h"A BasicAgent.cpp adja hozzá a következő
usingirányelveket.using namespace concurrency; using namespace std;A
_tmainfüggvényben hozzon létre egy concurrency::event objektumot, amely a feldolgozás végét jelzi.event e;Hozzon létre egy
callobjektumot, amely frissíti az ellenőrzőösszeget az adatok fogadásakor.// 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; }); });Ez az
callobjektum akkor is beállítja azeventobjektumot, amikor megkapja az üres sztringet a feldolgozás befejezésének jelzésére.Hozzon létre egy
file_readerobjektumot, amely beolvassa a fájlt test.txt, és a fájl tartalmát azcallobjektumba írja.file_reader reader("test.txt", calculate_checksum);Indítsa el az ügynököt, és várja meg, amíg befejeződik.
reader.start(); agent::wait(&reader);Várja meg, amíg az
callobjektum megkapja az összes adatot, és befejeződik.e.wait();Ellenőrizze a fájlolvasóban a hibákat. Ha nem történt hiba, számítsa ki az utolsó Adler-32 összeget, és nyomtassa ki az összeget a konzolra.
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; }
Az alábbi példa a teljes BasicAgent.cpp fájlt mutatja be.
// 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;
}
}
[Felső]
Mintabemenet
Ez a bemeneti fájl mintatartalma text.txt:
The quick brown fox
jumps
over the lazy dog
Mintakimenet
A mintabemenet használatakor a program a következő kimenetet állítja elő:
Adler-32 sum is fefb0d75
Robusztus programozás
Az adattagokhoz való egyidejű hozzáférés megakadályozása érdekében javasoljuk, hogy adjon hozzá olyan metódusokat, amelyek munkát végeznek a protected vagy private szakaszban az osztályán belül. Csak olyan metódusokat adhat hozzá, amelyek üzeneteket küldenek az ügynöknek vagy fogadnak az ügynöktől az public szakaszához az osztályban.
Mindig hívja meg a concurrency::agent::done metódust az ügynök befejezett állapotba helyezéséhez. Általában ezt a metódust a run metódus visszatérése előtt hívja meg.
Következő lépések
Egy másik példa egy ügynökalapú alkalmazásra: Útmutató: Csatlakozás használata a Holtpont megelőzéséhez.
Lásd még
Aszinkron ügynökök könyvtára
Aszinkron üzenetblokkok
Üzenetátadási függvények
Szinkronizálási adatstruktúrák
Útmutató: Join használata a holtpont megelőzéséhez