연습: 에이전트 기반 응용 프로그램 만들기

이 항목에서는 에이전트를 기반으로 한 기본 응용 프로그램을 만드는 방법에 대해 설명합니다.이 연습을 통해 텍스트 파일에서 비동기적으로 데이터를 읽는 에이전트를 만들 수 있습니다.응용 프로그램에서는 Adler-32 체크섬 알고리즘을 사용하여 해당 파일 콘텐츠의 체크섬을 계산합니다.

사전 요구 사항

이 연습을 완료하려면 다음 항목의 내용을 이해해야 합니다.

단원

이 연습에서는 다음과 같은 작업을 수행하는 방법을 보여 줍니다.

  • 콘솔 응용 프로그램 만들기

  • file_reader 클래스 만들기

  • 응용 프로그램에서 file_reader 클래스 사용

콘솔 응용 프로그램 만들기

이 단원에서는 프로그램에서 사용할 헤더 파일을 참조하는 Visual C++ 콘솔 응용 프로그램을 만드는 방법을 보여 줍니다.

Win32 콘솔 응용 프로그램 마법사를 사용하여 Visual C++ 응용 프로그램을 만들려면

  1. 파일 메뉴에서 새로 만들기, 프로젝트를 차례로 클릭하여 새 프로젝트 대화 상자를 표시합니다.

  2. 새 프로젝트 대화 상자의 프로젝트 형식 창에서 Visual C++ 노드를 선택한 다음, 템플릿 창에서 Win32 콘솔 응용 프로그램을 선택합니다.프로젝트 이름(예: BasicAgent)을 입력하고 확인을 클릭하여 Win32 콘솔 응용 프로그램 마법사를 표시합니다.

  3. Win32 콘솔 응용 프로그램 마법사 대화 상자에서 마침을 클릭합니다.

  4. stdafx.h에서 다음 코드를 추가합니다.

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

    기능을 포함 하는 헤더 파일 agents.h는 concurrency::agent 클래스입니다.

  5. 응용 프로그램을 빌드하고 실행하여 제대로 만들어졌는지 확인합니다.응용 프로그램을 빌드하려면 빌드 메뉴에서 솔루션 빌드를 클릭합니다.응용 프로그램이 제대로 빌드되면 디버그 메뉴에서 디버깅 시작을 클릭하여 응용 프로그램을 실행합니다.

Top

file_reader 클래스 만들기

이 단원에서는 file_reader 클래스를 만드는 방법을 보여 줍니다.런타임에서는 각 에이전트가 자체의 컨텍스트에서 작업을 수행하도록 예약합니다.따라서 동기적으로 작업을 수행하지만 다른 구성 요소와 비동기적으로 상호 작용하는 에이전트를 만들 수 있습니다.file_reader 클래스는 지정된 입력 파일에서 데이터를 읽고 지정된 대상 컴퓨터에 해당 파일의 데이터를 보냅니다.

file_reader 클래스를 만들려면

  1. 프로젝트에 새 C++ 헤더 파일을 추가합니다.이렇게 하려면 솔루션 탐색기에서 헤더 파일 노드를 마우스 오른쪽 단추로 클릭하고 추가를 클릭한 다음, 새 항목을 클릭합니다.그런 다음 템플릿 창에서 **헤더 파일 (.h)**을 선택합니다.새 항목 추가 대화 상자가 나타나면 이름 상자에 file_reader.h를 입력하고 추가를 클릭합니다.

  2. file_reader.h에서 다음 코드를 추가합니다.

    #pragma once
    
  3. file_reader.h에 agent에서 파생되는 file_reader 클래스를 만듭니다.

    class file_reader : public concurrency::agent
    {
    public:
    protected:
    private:
    };
    
  4. 다음 데이터 멤버를 클래스의 private 섹션에 추가합니다.

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

    _file_name 멤버는 에이전트가 읽는 파일 이름입니다._target 멤버인는 concurrency::ITarget 에이전트는 파일의 내용을 작성 하는 개체입니다._error 멤버는 에이전트의 수명 동안 발생하는 모든 오류를 저장합니다.

  5. file_reader 생성자에 대한 다음 코드를 file_reader 클래스의 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)
    {
    }
    

    각 생성자 오버로드는 file_reader 데이터 멤버를 설정합니다.두 번째 및 세 번째 생성자 오버로드를 통해 응용 프로그램에서 특정 스케줄러를 에이전트에 사용할 수 있습니다.첫 번째 오버로드는 에이전트에 기본 스케줄러를 사용합니다.

  6. get_error 메서드를 file_reader 클래스의 public 섹션에 추가합니다.

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

    get_error 메서드는 에이전트의 수명 동안 발생하는 모든 오류를 검색합니다.

  7. 구현에서 concurrency::agent::run 메서드에서 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();
    }
    

    run 메서드는 파일을 열고 데이터를 읽습니다.run 메서드는 예외 처리를 사용하여 파일 처리 중에 발생한 오류를 캡처합니다.

    이 메서드는 파일에서 데이터를 읽습니다 때마다 호출을 concurrency::asend 대상 버퍼에 데이터를 보낼 수 있는 기능입니다.또한 대상 버퍼에 빈 문자열을 보내 처리가 끝났음을 나타냅니다.

다음 예제에서는 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

응용 프로그램에서 file_reader 클래스 사용

이 단원에서는 file_reader 클래스를 사용하여 텍스트 파일의 내용을 읽는 방법을 보여 줍니다.만드는 방법을 보여 줍니다 있는 concurrency::call 이 파일의 데이터를 받고 해당 adler-32 체크섬을 계산 하는 개체입니다.

응용 프로그램에서 file_reader 클래스를 사용하려면

  1. BasicAgent.cpp에서 다음 #include 문을 추가합니다.

    #include "file_reader.h"
    
  2. BasicAgent.cpp에서 다음 using 지시문을 추가합니다.

    using namespace concurrency;
    using namespace std;
    
  3. _tmain 함수, 만들는 concurrency::event 개체 처리의 끝을 알립니다.

    event e;
    
  4. 데이터를 받으면 체크섬을 업데이트하는 call 개체를 만듭니다.

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

    또한 이 call 개체는 빈 문자열을 받으면 처리가 끝났음을 알리도록 event 개체를 설정합니다.

  5. test.txt 파일에서 읽고 이 파일의 내용을 call 개체에 쓰는 file_reader 개체를 만듭니다.

    file_reader reader("test.txt", calculate_checksum);
    
  6. 에이전트를 시작한 후 끝날 때까지 기다립니다.

    reader.start();
    agent::wait(&reader);
    
  7. call 개체가 모든 데이터를 받고 끝날 때까지 기다립니다.

    e.wait();
    
  8. 파일 판독기에서 오류를 확인합니다.오류가 없으면 최종 Adler-32 합계를 계산하여 콘솔에 출력합니다.

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

다음 예제에서는 전체 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

샘플 입력

다음 샘플은 text.txt 입력 파일의 내용입니다.

The quick brown fox
jumps
over the lazy dog

샘플 출력

샘플 입력과 함께 사용하여 이 프로그램을 실행하면 다음과 같은 결과가 출력됩니다.

Adler-32 sum is fefb0d75

강력한 프로그래밍

데이터 멤버에 동시에 액세스할 수 없게 하려면 작업을 수행하는 메서드를 클래스의 protected 또는 private 섹션에 추가하는 것이 좋습니다.에이전트에서 메시지를 받거나 에이전트에 메시지를 보내는 메서드만 클래스의 public 섹션에 추가하십시오.

항상 호출 하는 concurrency::agent:: 수행 에이전트 상태를 완료로 이동 하는 방법.일반적으로 run 메서드에서 반환하기 전에 이 메서드를 호출합니다.

다음 단계

에이전트 기반 응용 프로그램의 다른 예제를 보려면 연습: join을 사용하여 교착 상태 방지를 참조하십시오.

참고 항목

작업

연습: join을 사용하여 교착 상태 방지

개념

비동기 에이전트 라이브러리

비동기 메시지 블록

메시지 전달 함수

동기화 데이터 구조