Udostępnij za pośrednictwem


Instruktaż: Tworzenie aplikacji opartych na agenta

W tym temacie opisano sposób tworzenia podstawowych aplikacji opartych na agenta.W tym instruktażu można utworzyć agenta, który odczytuje dane z pliku tekstowego asynchronicznie.Aplikacja używa algorytmu sumę kontrolną 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

  • W aplikacji za pomocą klasy file_reader

Tworzenie aplikacji konsoli

W tej sekcji przedstawiono sposób tworzenia aplikacji konsoli Visual C++, która odwołuje się do plików nagłówka, który będzie używany w programie.

Aby utworzyć aplikację Visual C++ przy użyciu Kreatora aplikacji konsoli systemu Win32

  1. Na pliku menu, kliknij Nowy, a następnie kliknij przycisk projektu do wyświetlania Nowy projekt okno dialogowe.

  2. W Nowy projekt okno dialogowe Wybierz Visual C++ węzeł w typów projektów okienko, a następnie wybierz Win32 Console Application w szablonów okienka.Wpisz nazwę projektu, na przykład, BasicAgent, a następnie kliknij przycisk OK do wyświetlania Kreatora aplikacji konsoli Win32.

  3. W Kreatora aplikacji konsoli Win32 okno dialogowe, kliknij przycisk Zakończ.

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

  5. Sprawdź, czy aplikacja została pomyślnie utworzona, budowania i uruchamiając go.Do tworzenia aplikacji, z budować menu, kliknij Zbudować roztwór.Jeśli aplikacja tworzy się pomyślnie, należy uruchomić aplikację, klikając Start Debugging na debugowania menu.

Top

Tworzenie klasy file_reader

W tej sekcji przedstawiono sposób tworzenia file_reader klasy.Środowisko wykonawcze planuje wykonywanie pracy w swoim własnym kontekście każdego agenta.W związku z tym można utworzyć agenta, który wykonuje pracę synchronicznie, ale współdziała z innymi składnikami asynchronicznie.file_reader Klasy odczytuje dane z pliku wejściowego i przesyła dane z tego pliku do elementu docelowego danej.

Aby utworzyć klasę file_reader

  1. Nowy plik nagłówkowy C++ można dodać do projektu.Aby to zrobić, kliknij prawym przyciskiem myszy Pliki nagłówków węzeł w Solution Explorer, kliknij Dodaj, a następnie kliknij przycisk Nowego elementu.W szablonów okienku wybierz Pliku nagłówka (.h).W Dodaj nowy element okno dialogowe, typ file_reader.h w Nazwa a następnie kliknij przycisk Dodaj.

  2. W file_reader.h Dodaj następujący kod.

    #pragma once
    
  3. W file_reader.h, należy utworzyć klasę o nazwie file_reader który pochodzi od agent.

    class file_reader : public concurrency::agent
    {
    public:
    protected:
    private:
    };
    
  4. Dodaj następujący członkowie danych do private sekcji klasy.

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

    _file_name Członek jest nazwy pliku, który czyta agenta._target Jest concurrency::ITarget obiektów, że agent zapisuje zawartość pliku._error Członkowskich posiada wszelkie błędy występujące podczas użytkowania agenta.

  5. Dodaj następujący kod dla file_reader konstruktorów public sekcji 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 konstruktora file_reader danych członków.Przeciążenie konstruktora drugiego i trzeciego pozwala aplikacji za pomocą agenta użytkownika określonego harmonogramu.Pierwszy przeciążenie używa Harmonogram domyślny z agenta użytkownika.

  6. Dodaj get_error metody publiczne 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 agenta.

  7. Wdrożenie concurrency::agent::run metodę w protected sekcji 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 obsługę wyjątków do przechwytywania błędów, które wystąpiły podczas przetwarzania pliku.

    Przy każdym tej metody odczytuje dane z pliku, wywołuje concurrency::asend funkcji do wysyłania danych do buforu docelowego.Pusty ciąg wysyła do buforu docelowego do wskazywania końca przetwarzania.

Poniższy przykład pokazuje kompletną 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;
};

Top

W aplikacji za pomocą klasy file_reader

W tej sekcji przedstawiono sposób użycia file_reader klasy, aby odczytać zawartość pliku tekstowego.Również ilustruje sposób tworzenia concurrency::call obiekt, który odbiera dane tego pliku i oblicza sumy kontrolnej jego Adler-32.

Aby użyć klasy file_reader w aplikacji

  1. Dodaj następującą wartość w BasicAgent.cpp, #include instrukcji.

    #include "file_reader.h"
    
  2. Dodaj następującą wartość w BasicAgent.cpp, using dyrektyw.

    using namespace concurrency;
    using namespace std;
    
  3. W _tmain działać, tworzenie concurrency::event obiekt, który sygnalizuje koniec przetwarzania.

    event e;
    
  4. 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, aby zasygnalizować koniec przetwarzania.

  5. Tworzenie file_reader obiekt, który odczytuje test.txt pliku i zapisuje zawartość tego pliku, aby call obiektu.

    file_reader reader("test.txt", calculate_checksum);
    
  6. Uruchom agenta i zaczekać na jej zakończenie.

    reader.start();
    agent::wait(&reader);
    
  7. Poczekaj, aż call obiekt, aby otrzymywać wszystkie dane i Zakończ.

    e.wait();
    
  8. Sprawdź czytnik pliku błędów.Jeśli nie wystąpił żaden błąd, obliczyć sumy końcowe Adler-32 i drukować 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;
    }
    

Następujący przykład przedstawia pełny 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;
   }
}

Top

Przykładowe dane wejściowe

Jest to zawartość próbki text.txt pliku wejściowego:

The quick brown fox
jumps
over the lazy dog

Przykładowe dane wyjściowe

Gdy używana z wprowadzania próbki, ten program daje następujące dane wyjściowe:

Adler-32 sum is fefb0d75

Stabilne Programowanie

Aby zapobiec współbieżnego dostępu do danych członków, zaleca się dodanie metody, które wykonują pracę do protected lub private sekcji klasy.Dodaj tylko metody, które wysyłają lub odbierają wiadomości z agenta lub public sekcji klasy.

Zawsze używaj wywołania concurrency::agent:: Sporządzono metodę, aby przenieść Waszym stan wykonany.Zazwyczaj można wywołać tej metody przed zwróceniem z run metody.

Następne kroki

Innym przykładem aplikacji opartych na agentach, zobacz Instruktaż: Za pomocą sprzężenia, aby zapobiec zakleszczenia.

Zobacz też

Zadania

Instruktaż: Za pomocą sprzężenia, aby zapobiec zakleszczenia

Koncepcje

Biblioteka agentów asynchroniczne

Asynchroniczne blokuje wiadomości

Funkcji przekazywania wiadomości

Synchronizacja struktury danych