Megosztás a következőn keresztül:


Útmutató: Agent-Based-alkalmazás létrehozása

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

  1. A főmenüBen válassza azÚj>projekt> lehetőséget az Új projekt létrehozása párbeszédpanel megnyitásához.

  2. 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.

  3. 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 BasicAgent a projekt nevét, és szükség esetén adja meg a projekt helyét.

  4. A projekt létrehozásához válassza a Létrehozás gombot.

  5. 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

  1. A FájlmenüBen kattintson az Új, majd a Project gombra az Új projekt párbeszédpanel megjelenítéséhez.

  2. 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.

  3. 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

  1. 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.h a Név mezőbe, majd kattintson a Hozzáadás gombra.

  2. A file_reader.h fájlban adja hozzá a következő kódot.

    #pragma once
    
  3. A file_reader.h-ban hozzon létre egy osztályt file_reader , amely a következőből agentszármazik: .

    class file_reader : public concurrency::agent
    {
    public:
    protected:
    private:
    };
    
  4. Adja hozzá az alábbi adattagokat az private osztály szakaszához.

    std::string _file_name;
    concurrency::ITarget<std::string>& _target;
    concurrency::overwrite_buffer<std::exception> _error;
    

    A _file_name tag az az ügynök által beolvasott fájlnév. A _target tag egy egyidejűség::ITarget-objektum , amelybe az ügynök a fájl tartalmát írja. A _error tag tartalmazza az ügynök működése során előforduló bármilyen hibát.

  5. Adja hozzá a file_reader konstruktorok következő kódját a public osztály file_reader szakaszá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_reader adattagokat. 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.

  6. Adja hozzá a get_error metódust a file_reader osztály nyilvános szakaszához.

    bool get_error(std::exception& e)
    {
       return try_receive(_error, e);
    }
    

    A get_error metódus lekéri az ügynök élettartama során előforduló hibákat.

  7. 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

  1. A BasicAgent.cpp adja hozzá a következő #include utasítást.

    #include "file_reader.h"
    
  2. A BasicAgent.cpp adja hozzá a következő using irányelveket.

    using namespace concurrency;
    using namespace std;
    
  3. A _tmain függvényben hozzon létre egy concurrency::event objektumot, amely a feldolgozás végét jelzi.

    event e;
    
  4. Hozzon létre egy call objektumot, 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 call objektum akkor is beállítja az event objektumot, amikor megkapja az üres sztringet a feldolgozás befejezésének jelzésére.

  5. Hozzon létre egy file_reader objektumot, amely beolvassa a fájlt test.txt, és a fájl tartalmát az call objektumba írja.

    file_reader reader("test.txt", calculate_checksum);
    
  6. Indítsa el az ügynököt, és várja meg, amíg befejeződik.

    reader.start();
    agent::wait(&reader);
    
  7. Várja meg, amíg az call objektum megkapja az összes adatot, és befejeződik.

    e.wait();
    
  8. 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