Udostępnij za pośrednictwem


Wskazówki: tworzenie aplikacji opartej o agentów

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

  • Korzystając z klasy file_reader w aplikacji

Tworzenie aplikacji konsoli

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

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

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

  2. W Nowy projekt okno dialogowe, wybierz Visual C++ węzeł w typów projektów okienka, a następnie wybierz Win32 Console Application w Szablony 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ł pomyślnie utworzony przez tworzenie i uruchamianie go.Do tworzenia aplikacji, z budować menu, kliknij przycisk Budowania rozwiązania.Jeśli aplikacja tworzy pomyślnie, uruchomić aplikację, klikając Rozpocząć debugowanie na Debug menu.

[U góry]

Tworzenie klasy file_reader

W tej sekcji przedstawiono sposób tworzenia file_reader klasy.Środowisko wykonawcze planuje każdego agenta do wykonywania pracy w swoim własnym kontekście.W związku z tym można utworzyć agenta, który wykonuje pracę synchronicznie, lecz współdziała z innymi składnikami asynchronicznie.file_reader Klasy odczytuje dane z określonego pliku wejściowego i wysyła dane z tego pliku do części określonej wartości docelowej.

Aby utworzyć klasę file_reader

  1. Dodaj nowy plik nagłówka C++ do projektu.Aby to zrobić, kliknij prawym przyciskiem myszy Pliki nagłówków węzła w Solution Explorer, kliknij Dodaj, a następnie kliknij przycisk Dodaje się ppkt.W Szablony okienka, 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 wywodzi się z agent.

    class file_reader : public concurrency::agent
    {
    public:
    protected:
    private:
    };
    
  4. Dodaj następujące elementy danych do private część swojej klasy.

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

    _file_name Członek jest agent odczytuje z nazwą pliku._target Element członkowski jest concurrency::ITarget że agent zapisuje zawartość pliku do obiektu._error Członkowskich posiada wszelkie błędy występujące podczas użytkowania programu agent.

  5. Dodaj następujący kod dla file_reader konstruktorów public części 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 Konstruktor file_reader danych członków.Przeciążenie Konstruktor drugiego i trzeciego umożliwia aplikacji do korzystania z agenta określonego harmonogramu.Pierwszy przeciążenie używa Harmonogram domyślny agent.

  6. Dodaj get_error metoda do publicznej 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 programu agent.

  7. Implementacja concurrency::agent::run metoda w protected część swojej 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 do przechwytywania błędów, które wystąpiły podczas przetwarzania pliku obsługi wyjątków.

    Wywołuje każdorazowo ta metoda odczytuje dane z pliku concurrency::asend funkcję wysyłania danych do buforu docelowego.Wysyła ciąg pusty do buforu docelowego do wskazywania końca przetwarzania.

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

[U góry]

Korzystając z klasy file_reader w aplikacji

W tej sekcji przedstawiono sposób użycia file_reader klasy do odczytu zawartości pliku tekstowego.Również pokazuje, jak utworzyć concurrency::call obiekt, który odbiera dane tego pliku i oblicza sumę kontrolną jego Adler-32.

Aby użyć klasy file_reader w aplikacji

  1. Dodać następujący wpis w BasicAgent.cpp, #include instrukcji.

    #include "file_reader.h"
    
  2. Dodać następujący wpis w BasicAgent.cpp, using dyrektyw.

    using namespace concurrency;
    using namespace std;
    
  3. W _tmain funkcji, 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 znaków do sygnalizowania zakończenia przetwarzania.

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

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

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

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

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

[U góry]

Przykładowe dane wejściowe

To jest zawartość próbki text.txt pliku wejściowego:

  

Przykładowe dane wyjściowe

Gdy używana z przykładowe dane wejściowe, to program wyświetli:

  

Niezawodne programowanie

Aby zapobiec równoczesny dostęp do danych członków, firma Microsoft zaleca, aby dodać metody, które wykonują pracę przy protected lub private część swojej klasy.Metody, które wysyłają lub odbierają wiadomości z agenta lub dodawać tylko public część swojej klasy.

Wywoływanie zawsze concurrency::agent:: Sporządzono metoda przeniesienia swojego agenta do stanu wykonanego.Zazwyczaj można wywołać tej metody przed zwróceniem z run metody.

Następne kroki

Innym przykładem aplikacji opartych na agentach, zobacz Wskazówki: korzystanie ze złączy w celu zapobiegania zakleszczeniom.

Zobacz też

Zadania

Wskazówki: korzystanie ze złączy w celu zapobiegania zakleszczeniom

Koncepcje

Biblioteka agentów asynchronicznych

Bloki komunikatów asynchronicznych

Funkcje przekazywania komunikatów

Struktury danych synchronizacji