연습: 에이전트 기반 응용 프로그램 만들기
이 항목에서는 에이전트를 기반으로 한 기본 응용 프로그램을 만드는 방법에 대해 설명합니다.이 연습을 통해 텍스트 파일에서 비동기적으로 데이터를 읽는 에이전트를 만들 수 있습니다.응용 프로그램에서는 Adler-32 체크섬 알고리즘을 사용하여 해당 파일 콘텐츠의 체크섬을 계산합니다.
사전 요구 사항
이 연습을 완료하려면 다음 항목의 내용을 이해해야 합니다.
단원
이 연습에서는 다음과 같은 작업을 수행하는 방법을 보여 줍니다.
콘솔 응용 프로그램 만들기
file_reader 클래스 만들기
응용 프로그램에서 file_reader 클래스 사용
콘솔 응용 프로그램 만들기
이 단원에서는 프로그램에서 사용할 헤더 파일을 참조하는 Visual C++ 콘솔 응용 프로그램을 만드는 방법을 보여 줍니다.
Win32 콘솔 응용 프로그램 마법사를 사용하여 Visual C++ 응용 프로그램을 만들려면
파일 메뉴에서 새로 만들기, 프로젝트를 차례로 클릭하여 새 프로젝트 대화 상자를 표시합니다.
새 프로젝트 대화 상자의 프로젝트 형식 창에서 Visual C++ 노드를 선택한 다음, 템플릿 창에서 Win32 콘솔 응용 프로그램을 선택합니다.프로젝트 이름(예: BasicAgent)을 입력하고 확인을 클릭하여 Win32 콘솔 응용 프로그램 마법사를 표시합니다.
Win32 콘솔 응용 프로그램 마법사 대화 상자에서 마침을 클릭합니다.
stdafx.h에서 다음 코드를 추가합니다.
#include <agents.h> #include <string> #include <iostream> #include <algorithm>
기능을 포함 하는 헤더 파일 agents.h는 concurrency::agent 클래스입니다.
응용 프로그램을 빌드하고 실행하여 제대로 만들어졌는지 확인합니다.응용 프로그램을 빌드하려면 빌드 메뉴에서 솔루션 빌드를 클릭합니다.응용 프로그램이 제대로 빌드되면 디버그 메뉴에서 디버깅 시작을 클릭하여 응용 프로그램을 실행합니다.
Top
file_reader 클래스 만들기
이 단원에서는 file_reader 클래스를 만드는 방법을 보여 줍니다.런타임에서는 각 에이전트가 자체의 컨텍스트에서 작업을 수행하도록 예약합니다.따라서 동기적으로 작업을 수행하지만 다른 구성 요소와 비동기적으로 상호 작용하는 에이전트를 만들 수 있습니다.file_reader 클래스는 지정된 입력 파일에서 데이터를 읽고 지정된 대상 컴퓨터에 해당 파일의 데이터를 보냅니다.
file_reader 클래스를 만들려면
프로젝트에 새 C++ 헤더 파일을 추가합니다.이렇게 하려면 솔루션 탐색기에서 헤더 파일 노드를 마우스 오른쪽 단추로 클릭하고 추가를 클릭한 다음, 새 항목을 클릭합니다.그런 다음 템플릿 창에서 **헤더 파일 (.h)**을 선택합니다.새 항목 추가 대화 상자가 나타나면 이름 상자에 file_reader.h를 입력하고 추가를 클릭합니다.
file_reader.h에서 다음 코드를 추가합니다.
#pragma once
file_reader.h에 agent에서 파생되는 file_reader 클래스를 만듭니다.
class file_reader : public concurrency::agent { public: protected: private: };
다음 데이터 멤버를 클래스의 private 섹션에 추가합니다.
std::string _file_name; concurrency::ITarget<std::string>& _target; concurrency::overwrite_buffer<std::exception> _error;
_file_name 멤버는 에이전트가 읽는 파일 이름입니다._target 멤버인는 concurrency::ITarget 에이전트는 파일의 내용을 작성 하는 개체입니다._error 멤버는 에이전트의 수명 동안 발생하는 모든 오류를 저장합니다.
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 데이터 멤버를 설정합니다.두 번째 및 세 번째 생성자 오버로드를 통해 응용 프로그램에서 특정 스케줄러를 에이전트에 사용할 수 있습니다.첫 번째 오버로드는 에이전트에 기본 스케줄러를 사용합니다.
get_error 메서드를 file_reader 클래스의 public 섹션에 추가합니다.
bool get_error(std::exception& e) { return try_receive(_error, e); }
get_error 메서드는 에이전트의 수명 동안 발생하는 모든 오류를 검색합니다.
구현에서 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 클래스를 사용하려면
BasicAgent.cpp에서 다음 #include 문을 추가합니다.
#include "file_reader.h"
BasicAgent.cpp에서 다음 using 지시문을 추가합니다.
using namespace concurrency; using namespace std;
에 _tmain 함수, 만들는 concurrency::event 개체 처리의 끝을 알립니다.
event e;
데이터를 받으면 체크섬을 업데이트하는 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 개체를 설정합니다.
test.txt 파일에서 읽고 이 파일의 내용을 call 개체에 쓰는 file_reader 개체를 만듭니다.
file_reader reader("test.txt", calculate_checksum);
에이전트를 시작한 후 끝날 때까지 기다립니다.
reader.start(); agent::wait(&reader);
call 개체가 모든 데이터를 받고 끝날 때까지 기다립니다.
e.wait();
파일 판독기에서 오류를 확인합니다.오류가 없으면 최종 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을 사용하여 교착 상태 방지를 참조하십시오.