Delen via


Overzicht: Een Agent-Based-toepassing maken

In dit onderwerp wordt beschreven hoe u een eenvoudige toepassing op basis van agents maakt. In dit scenario kunt u een agent maken die gegevens asynchroon leest uit een tekstbestand. De toepassing maakt gebruik van het Adler-32-algoritme om de controlesom van de inhoud van dat bestand te berekenen.

Vereiste voorwaarden

U moet de volgende onderwerpen begrijpen om dit scenario te voltooien:

Afdelingen

In deze stap-voor-stap handleiding ziet u hoe u de volgende taken uitvoert:

De consoletoepassing maken

In deze sectie wordt beschreven hoe u een C++-consoletoepassing maakt die verwijst naar de headerbestanden die door het programma worden gebruikt. De eerste stappen variƫren, afhankelijk van de versie van Visual Studio die u gebruikt. Als u de documentatie voor uw voorkeursversie van Visual Studio wilt bekijken, gebruikt u het besturingselement voor versie-selector . Deze bevindt zich boven aan de inhoudsopgave op deze pagina.

Een C++-consoletoepassing maken in Visual Studio

  1. Kies bestand>nieuw>project in het hoofdmenu om het dialoogvenster Een nieuw project maken te openen.

  2. Stel bovenaan het dialoogvenster Taal in op C++, stel Platform in op Windows en stel Projecttype in op Console.

  3. Kies Console-app in de gefilterde lijst met projecttypen en kies vervolgens Volgende. Voer BasicAgent op de volgende pagina de naam voor het project in en geef desgewenst de projectlocatie op.

  4. Kies de knop Maken om het project te maken.

  5. Klik met de rechtermuisknop op het projectknooppunt in Solution Explorer en kies Eigenschappen. Onder Configuratie-eigenschappen>C/C++>Vooraf gecompileerde headers>Vooraf gecompileerde header kies Aanmaken.

Een C++-consoletoepassing maken in Visual Studio 2017 en eerder

  1. Klik in het menu Bestand op Nieuw en klik vervolgens op Project om het dialoogvenster Nieuw project weer te geven.

  2. Selecteer in het dialoogvenster Nieuw project het knooppunt Visual C++ in het deelvenster Projecttypen en selecteer vervolgens Win32-consoletoepassing in het deelvenster Sjablonen . Typ bijvoorbeeld een naam voor het project BasicAgenten klik vervolgens op OK om de wizard Win32-consoletoepassing weer te geven.

  3. Klik in het dialoogvenster wizard Win32-consoletoepassing op Voltooien.

Het headerbestand bijwerken

Voeg in het bestand pch.h (stdafx.h in Visual Studio 2017 en eerder) de volgende code toe:

#include <agents.h>
#include <string>
#include <iostream>
#include <algorithm>

De headerbestand agents.h bevat de functionaliteit van de gelijktijdigheid:::agentklasse .

De toepassing controleren

Controleer ten slotte of de toepassing succesvol is gemaakt door deze te bouwen en uit te voeren. Als u de toepassing wilt bouwen, klikt u in het menu Bouwen op Oplossing bouwen. Als de toepassing succesvol is gebouwd, voert u de toepassing uit door te klikken op Foutopsporing starten via het menu Foutopsporing.

[Boven]

De file_reader-klasse maken

In deze sectie ziet u hoe u de file_reader klasse maakt. De runtime plant elke agent om werk uit te voeren in zijn eigen context. Daarom kunt u een agent maken die synchroon werkt, maar asynchroon communiceert met andere onderdelen. De file_reader klasse leest gegevens uit een bepaald invoerbestand en verzendt gegevens uit dat bestand naar een bepaald doelonderdeel.

De file_reader-klasse maken

  1. Voeg een nieuw C++-headerbestand toe aan uw project. Klik hiervoor met de rechtermuisknop op het knooppunt Header Files in Solution Explorer, klik op Toevoegen en klik vervolgens op Nieuw item. Selecteer in het deelvenster Sjablonende optie Koptekstbestand (.h).). Voer in het dialoogvenster Nieuw item toevoegen het vak file_reader.h in en klik vervolgens op Toevoegen.

  2. Voeg in file_reader.h de volgende code toe.

    #pragma once
    
  3. Maak in file_reader.h een klasse met de naam file_reader die is afgeleid van agent.

    class file_reader : public concurrency::agent
    {
    public:
    protected:
    private:
    };
    
  4. Voeg de volgende gegevensleden toe aan de private sectie van uw klas.

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

    Het _file_name lid is de bestandsnaam waaruit de agent leest. Het _target lid is een concurrentie::ITarget-object waarin de agent de inhoud van het bestand schrijft. Het _error lid bevat elke fout die optreedt tijdens de levensduur van de agent.

  5. Voeg de volgende code voor de file_reader constructors toe aan de public sectie van de file_reader klasse.

    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)
    {
    }
    

    Elke constructoroverload stelt de file_reader gegevensleden in. Met de tweede en derde constructoroverbelasting kan uw toepassing een specifieke taakplanner gebruiken met uw agent. De eerste overbelasting maakt gebruik van de standaardplanner met uw agent.

  6. Voeg de get_error methode toe aan de openbare sectie van de file_reader klasse.

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

    De get_error methode haalt elke fout op die zich voordoet tijdens de levensduur van de agent.

  7. Implementeer de gelijktijdigheid::agent::run-methode in het protected gedeelte van uw klasse.

    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();
    }
    

De run methode opent het bestand en leest er gegevens uit. De run methode maakt gebruik van uitzonderingsafhandeling om fouten vast te leggen die optreden tijdens het verwerken van bestanden.

Telkens wanneer deze methode gegevens uit het bestand leest, wordt de gelijktijdigheidsfunctie::asend aangeroepen om die gegevens naar de doelbuffer te verzenden. De lege tekenreeks wordt naar de doelbuffer verzonden om het einde van de verwerking aan te geven.

In het volgende voorbeeld ziet u de volledige inhoud van 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;
};

[Boven]

De file_reader-klasse in de toepassing gebruiken

In deze sectie ziet u hoe u de file_reader klasse gebruikt om de inhoud van een tekstbestand te lezen. Het laat ook zien hoe u een object concurrency::call maakt dat deze bestandsgegevens ontvangt en de Adler-32-controlesom berekent.

De file_reader-klasse in uw toepassing gebruiken

  1. Voeg in BasicAgent.cpp de volgende #include instructie toe.

    #include "file_reader.h"
    
  2. Voeg in BasicAgent.cpp de volgende using instructies toe.

    using namespace concurrency;
    using namespace std;
    
  3. Maak in de _tmain-functie een concurrency::event-object aan dat het einde van de verwerking aangeeft.

    event e;
    
  4. Maak een call object waarmee de controlesom wordt bijgewerkt wanneer deze gegevens ontvangt.

    // 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;
       });
    });
    

    Met dit call object wordt ook het event object ingesteld wanneer deze de lege tekenreeks ontvangt om het einde van de verwerking aan te geven.

  5. Maak een file_reader object dat wordt gelezen uit het bestand test.txt en schrijft de inhoud van dat bestand naar het call object.

    file_reader reader("test.txt", calculate_checksum);
    
  6. Start de agent en wacht tot deze is voltooid.

    reader.start();
    agent::wait(&reader);
    
  7. Wacht totdat het call object alle gegevens ontvangt en klaar is.

    e.wait();
    
  8. Controleer de bestandslezer op fouten. Wanneer er geen fout is opgetreden, bereken de uiteindelijke Adler-32 som en druk de som af op de 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;
    }
    

In het volgende voorbeeld ziet u het volledige BasicAgent.cpp-bestand.

// 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;
   }
}

[Boven]

Voorbeeldinvoer

Dit is de voorbeeldinhoud van het invoerbestand text.txt:

The quick brown fox
jumps
over the lazy dog

Voorbeelduitvoer

Wanneer dit programma wordt gebruikt met de voorbeeldinvoer, produceert dit programma de volgende uitvoer:

Adler-32 sum is fefb0d75

Robuuste programmering

Om gelijktijdige toegang tot gegevensleden te voorkomen, raden we u aan methoden toe te voegen die werk uitvoeren aan de protected of private sectie van uw klas. Voeg alleen methoden toe waarmee berichten naar of van de agent worden verzonden of ontvangen naar de public sectie van uw klasse.

Roep altijd de concurrency::agent::done methode aan om uw agent naar de voltooide status te verplaatsen. Normaal gesproken roept u deze methode aan voordat u terugkeert vanuit de run methode.

Volgende stappen

Zie Handleiding: Join gebruiken om impasses te voorkomen voor een ander voorbeeld van een agentgebaseerde toepassing.

Zie ook

Bibliotheek met asynchrone agents
Asynchrone berichtblokken
Functies voor het doorgeven van berichten
Synchronisatiegegevensstructuren
Handleiding: Join gebruiken om deadlock te voorkomen