연습: 데이터 흐름 에이전트 만들기Walkthrough: Creating a Dataflow Agent

이 문서에서는 제어 흐름 대신 데이터 흐름을 기반으로 하는 에이전트 기반 응용 프로그램을 만드는 방법을 보여 줍니다.This document demonstrates how to create agent-based applications that are based on dataflow, instead of control flow.

제어 흐름 은 프로그램의 작업 실행 순서를 나타냅니다.Control flow refers to the execution order of operations in a program. 제어 흐름은 조건문, 루프 등의 제어 구조를 사용 하 여 규제 됩니다.Control flow is regulated by using control structures such as conditional statements, loops, and so on. 또는 데이터 흐름 은 필요한 모든 데이터를 사용할 수 있는 경우에만 계산을 수행 하는 프로그래밍 모델을 나타냅니다.Alternatively, dataflow refers to a programming model in which computations are made only when all required data is available. 데이터 흐름 프로그래밍 모델은 프로그램의 독립적인 구성 요소가 메시지를 전송 하 여 서로 통신 하는 메시지 전달 개념과 관련이 있습니다.The dataflow programming model is related to the concept of message passing, in which independent components of a program communicate with one another by sending messages.

비동기 에이전트는 제어 흐름 및 데이터 흐름 프로그래밍 모델을 모두 지원 합니다.Asynchronous agents support both the control-flow and dataflow programming models. 대부분의 경우에는 제어 흐름 모델이 적합 하지만 데이터 흐름 모델은 에이전트에서 데이터를 수신 하 고 해당 데이터의 페이로드를 기반으로 하는 작업을 수행 하는 경우 등에 적합 합니다.Although the control-flow model is appropriate in many cases, the dataflow model is appropriate in others, for example, when an agent receives data and performs an action that is based on the payload of that data.

필수 구성 요소Prerequisites

이 연습을 시작 하기 전에 다음 문서를 읽어 보세요.Read the following documents before you start this walkthrough:

섹션Sections

이 연습에는 다음과 같은 섹션이 있습니다.This walkthrough contains the following sections:

기본 제어 흐름 에이전트 만들기Creating a Basic Control-Flow Agent

control_flow_agent 클래스를 정의 하는 다음 예제를 참조 하세요.Consider the following example that defines the control_flow_agent class. control_flow_agent 클래스는 세 개의 메시지 버퍼 (하나의 입력 버퍼와 두 개의 출력 버퍼)에 적용 됩니다.The control_flow_agent class acts on three message buffers: one input buffer and two output buffers. run 메서드는 루프에서 소스 메시지 버퍼를 읽고 조건문을 사용 하 여 프로그램 실행 흐름을 지시 합니다.The run method reads from the source message buffer in a loop and uses a conditional statement to direct the flow of program execution. 에이전트는 0이 아닌 값에 대해 카운터 하나를 증가 시키고 0이 아닌 양수 값에 대해 다른 카운터를 증가 시킵니다.The agent increments one counter for non-zero, negative values and increments another counter for non-zero, positive values. 에이전트가 센티널 값 0을 받은 후에는 카운터의 값을 출력 메시지 버퍼로 보냅니다.After the agent receives the sentinel value of zero, it sends the values of the counters to the output message buffers. negativespositives 메서드를 사용 하 여 응용 프로그램에서 에이전트의 음수 및 양수 값의 개수를 읽을 수 있습니다.The negatives and positives methods enable the application to read the counts of negative and positive values from the agent.

// A basic agent that uses control-flow to regulate the order of program 
// execution. This agent reads numbers from a message buffer and counts the 
// number of positive and negative values.
class control_flow_agent : public agent
{
public:
   explicit control_flow_agent(ISource<int>& source)
      : _source(source)
   {
   }

   // Retrieves the count of negative numbers that the agent received.
   size_t negatives() 
   {
      return receive(_negatives);
   }

   // Retrieves the count of positive numbers that the agent received.
   size_t positives()
   {
      return receive(_positives);
   }

protected:
   void run()
   {
      // Counts the number of negative and positive values that
      // the agent receives.
      size_t negative_count = 0;
      size_t positive_count = 0;

      // Read from the source buffer until we receive
      // the sentinel value of 0.
      int value = 0;      
      while ((value = receive(_source)) != 0)
      {
         // Send negative values to the first target and
         // non-negative values to the second target.
         if (value < 0)
            ++negative_count;
         else
            ++positive_count;
      }

      // Write the counts to the message buffers.
      send(_negatives, negative_count);
      send(_positives, positive_count);

      // Set the agent to the completed state.
      done();
   }
private:
   // Source message buffer to read from.
   ISource<int>& _source;

   // Holds the number of negative and positive numbers that the agent receives.
   single_assignment<size_t> _negatives;
   single_assignment<size_t> _positives;
};

이 예에서는 에이전트에서 제어 흐름의 기본 사용을 설정 하지만 제어 흐름 기반 프로그래밍의 직렬 특성을 보여 줍니다.Although this example makes basic use of control flow in an agent, it demonstrates the serial nature of control-flow-based programming. 입력 메시지 버퍼에서 여러 메시지를 사용할 수 있는 경우에도 각 메시지는 순차적으로 처리 되어야 합니다.Each message must be processed sequentially, even though multiple messages might be available in the input message buffer. 데이터 흐름 모델을 사용 하면 조건문의 두 분기가 동시에 평가 됩니다.The dataflow model enables both branches of the conditional statement to evaluate concurrently. 데이터 흐름 모델을 사용 하면 데이터를 사용할 수 있게 되 면 데이터에 대해 작동 하는 더 복잡 한 메시징 네트워크를 만들 수도 있습니다.The dataflow model also enables you to create more complex messaging networks that act on data as it becomes available.

[맨 위로 이동][Top]

기본 데이터 흐름 에이전트 만들기Creating a Basic Dataflow Agent

이 섹션에서는 데이터 흐름 모델을 사용 하 여 동일한 작업을 수행 하도록 control_flow_agent 클래스를 변환 하는 방법을 보여 줍니다.This section shows how to convert the control_flow_agent class to use the dataflow model to perform the same task.

데이터 흐름 에이전트는 각각 특정 용도로 사용 되는 메시지 버퍼의 네트워크를 만들어 작동 합니다.The dataflow agent works by creating a network of message buffers, each of which serves a specific purpose. 특정 메시지 블록은 필터 함수를 사용 하 여 페이로드를 기준으로 메시지를 수락 하거나 거부 합니다.Certain message blocks use a filter function to accept or reject a message on the basis of its payload. Filter 함수는 메시지 블록이 특정 값만 받도록 합니다.A filter function ensures that a message block receives only certain values.

제어 흐름 에이전트를 데이터 흐름 에이전트로 변환 하려면To convert the control-flow agent to a dataflow agent

  1. control_flow_agent 클래스의 본문을 다른 클래스 (예: dataflow_agent로 복사 합니다.Copy the body of the control_flow_agent class to another class, for example, dataflow_agent. 또는 control_flow_agent 클래스의 이름을 바꿀 수 있습니다.Alternatively, you can rename the control_flow_agent class.

  2. run 메서드에서 receive를 호출 하는 루프의 본문을 제거 합니다.Remove the body of the loop that calls receive from the run method.

void run()
{
   // Counts the number of negative and positive values that
   // the agent receives.
   size_t negative_count = 0;
   size_t positive_count = 0;


   // Write the counts to the message buffers.
   send(_negatives, negative_count);
   send(_positives, positive_count);

   // Set the agent to the completed state.
   done();
}
  1. run 메서드에서 negative_countpositive_count변수를 초기화 한 후 활성 작업의 수를 추적 하는 countdown_event 개체를 추가 합니다.In the run method, after the initialization of the variables negative_count and positive_count, add a countdown_event object that tracks the count of active operations.
// Tracks the count of active operations.
countdown_event active;
// An event that is set by the sentinel.
event received_sentinel;

countdown_event 클래스는이 항목의 뒷부분에 나와 있습니다.The countdown_event class is shown later in this topic.

  1. 데이터 흐름 네트워크에 참여 하는 메시지 버퍼 개체를 만듭니다.Create the message buffer objects that will participate in the dataflow network.
 //
 // Create the members of the dataflow network.
 //

 // Increments the active counter.
 transformer<int, int> increment_active(
    [&active](int value) -> int {
       active.add_count();
       return value;
    });

 // Increments the count of negative values.
 call<int> negatives(
    [&](int value) {
       ++negative_count;
       // Decrement the active counter.
       active.signal();
    },
    [](int value) -> bool {
       return value < 0;
    });

 // Increments the count of positive values.
 call<int> positives(
    [&](int value) {
       ++positive_count;
       // Decrement the active counter.
       active.signal();
    },
    [](int value) -> bool {
       return value > 0;
    });

 // Receives only the sentinel value of 0.
 call<int> sentinel(
    [&](int value) {            
       // Decrement the active counter.
       active.signal();
       // Set the sentinel event.
       received_sentinel.set();
    },
    [](int value) { 
       return value == 0; 
    });

 // Connects the _source message buffer to the rest of the network.
 unbounded_buffer<int> connector;
  1. 메시지 버퍼를 연결 하 여 네트워크를 구성 합니다.Connect the message buffers to form a network.
//
// Connect the network.
//

// Connect the internal nodes of the network.
connector.link_target(&negatives);
connector.link_target(&positives);
connector.link_target(&sentinel);
increment_active.link_target(&connector);

// Connect the _source buffer to the internal network to 
// begin data flow.
_source.link_target(&increment_active);
  1. eventcountdown event 개체가 설정 될 때까지 기다립니다.Wait for the event and countdown event objects to be set. 이러한 이벤트는 에이전트가 센티널 값을 받고 모든 작업이 완료 되었음을 신호로 알립니다.These events signal that the agent has received the sentinel value and that all operations have finished.
// Wait for the sentinel event and for all operations to finish.
received_sentinel.wait();
active.wait();

다음 다이어그램에서는 dataflow_agent 클래스에 대 한 전체 데이터 흐름 네트워크를 보여 줍니다.The following diagram shows the complete dataflow network for the dataflow_agent class:

데이터 흐름 네트워크The dataflow network

다음 표에서는 네트워크의 멤버를 설명합니다.The following table describes the members of the network.

멤버Member 설명Description
increment_active 활성 이벤트 카운터를 증가 시키고 입력 값을 네트워크의 나머지 부분에 전달 하는 concurrency:: 변환기 개체입니다.A concurrency::transformer object that increments the active event counter and passes the input value to the rest of the network.
negatives, positivesnegatives, positives concurrency:: 숫자의 수를 늘리고 활성 이벤트 카운터를 감소 시키는 개체를 호출 합니다.concurrency::call objects that increment the count of numbers and decrements the active event counter. 각각의 개체는 필터를 사용 하 여 음수 또는 양수를 허용 합니다.The objects each use a filter to accept either negative numbers or positive numbers.
sentinel 센티널 값 0만 허용 하 고 활성 이벤트 카운터를 감소 시키는 concurrency:: call 개체입니다.A concurrency::call object that accepts only the sentinel value of zero and decrements the active event counter.
connector 원본 메시지 버퍼를 내부 네트워크에 연결 하는 concurrency:: unbounded_buffer 개체입니다.A concurrency::unbounded_buffer object that connects the source message buffer to the internal network.

run 메서드는 별도의 스레드에서 호출 되기 때문에 네트워크가 완전히 연결 되기 전에 다른 스레드에서 네트워크에 메시지를 보낼 수 있습니다.Because the run method is called on a separate thread, other threads can send messages to the network before the network is fully connected. _source 데이터 멤버는 응용 프로그램에서 에이전트로 전송 되는 모든 입력을 버퍼링 하는 unbounded_buffer 개체입니다.The _source data member is an unbounded_buffer object that buffers all input that is sent from the application to the agent. 네트워크에서 모든 입력 메시지를 처리 하는지 확인 하기 위해 에이전트는 먼저 네트워크의 내부 노드를 연결한 다음 해당 네트워크의 시작 connector_source 데이터 멤버에 연결 합니다.To make sure that the network processes all input messages, the agent first links the internal nodes of the network and then links the start of that network, connector, to the _source data member. 그러면 네트워크가 구성 될 때 메시지가 처리 되지 않습니다.This guarantees that messages do not get processed as the network is being formed.

이 예제의 네트워크는 제어 흐름이 아닌 데이터 흐름을 기반으로 하기 때문에 네트워크는 각 입력 값의 처리를 완료 하 고 센티널 노드가 해당 값을 받았음을 에이전트와 통신 해야 합니다.Because the network in this example is based on dataflow, rather than on control-flow, the network must communicate to the agent that it has finished processing each input value and that the sentinel node has received its value. 이 예제에서는 countdown_event 개체를 사용 하 여 모든 입력 값이 처리 되었음을 알리고, concurrency:: event 개체를 사용 하 여 센티널 노드가 해당 값을 받았음을 표시 합니다.This example uses a countdown_event object to signal that all input values have been processed and a concurrency::event object to indicate that the sentinel node has received its value. countdown_event 클래스는 카운터 값이 0에 도달할 때 신호를 보내기 위해 event 개체를 사용 합니다.The countdown_event class uses an event object to signal when a counter value reaches zero. 데이터 흐름 네트워크의 헤드는 값을 받을 때마다 카운터를 증가 시킵니다.The head of the dataflow network increments the counter every time that it receives a value. 네트워크의 모든 터미널 노드는 입력 값을 처리 한 후 카운터를 감소 시킵니다.Every terminal node of the network decrements the counter after it processes the input value. 에이전트는 데이터 흐름 네트워크를 구성 하 고 나면 센티널 노드가 event 개체를 설정 하 고 countdown_event 개체가 카운터가 0에 도달 했음을 알리기 위해 대기 합니다.After the agent forms the dataflow network, it waits for the sentinel node to set the event object and for the countdown_event object to signal that its counter has reached zero.

다음 예에서는 control_flow_agent, dataflow_agentcountdown_event 클래스를 보여 줍니다.The following example shows the control_flow_agent, dataflow_agent, and countdown_event classes. wmain 함수는 control_flow_agentdataflow_agent 개체를 만들고 send_values 함수를 사용 하 여 일련의 난수 값을 에이전트로 보냅니다.The wmain function creates a control_flow_agent and a dataflow_agent object and uses the send_values function to send a series of random values to the agents.

// dataflow-agent.cpp
// compile with: /EHsc 
#include <windows.h>
#include <agents.h>
#include <iostream>
#include <random>

using namespace concurrency;
using namespace std;

// A basic agent that uses control-flow to regulate the order of program 
// execution. This agent reads numbers from a message buffer and counts the 
// number of positive and negative values.
class control_flow_agent : public agent
{
public:
   explicit control_flow_agent(ISource<int>& source)
      : _source(source)
   {
   }

   // Retrieves the count of negative numbers that the agent received.
   size_t negatives() 
   {
      return receive(_negatives);
   }

   // Retrieves the count of positive numbers that the agent received.
   size_t positives()
   {
      return receive(_positives);
   }

protected:
   void run()
   {
      // Counts the number of negative and positive values that
      // the agent receives.
      size_t negative_count = 0;
      size_t positive_count = 0;

      // Read from the source buffer until we receive
      // the sentinel value of 0.
      int value = 0;      
      while ((value = receive(_source)) != 0)
      {
         // Send negative values to the first target and
         // non-negative values to the second target.
         if (value < 0)
            ++negative_count;
         else
            ++positive_count;
      }

      // Write the counts to the message buffers.
      send(_negatives, negative_count);
      send(_positives, positive_count);

      // Set the agent to the completed state.
      done();
   }
private:
   // Source message buffer to read from.
   ISource<int>& _source;

   // Holds the number of negative and positive numbers that the agent receives.
   single_assignment<size_t> _negatives;
   single_assignment<size_t> _positives;
};

// A synchronization primitive that is signaled when its 
// count reaches zero.
class countdown_event
{
public:
   countdown_event(unsigned int count = 0L)
      : _current(static_cast<long>(count)) 
   {
      // Set the event if the initial count is zero.
      if (_current == 0L)
         _event.set();
   }
     
   // Decrements the event counter.
   void signal() {
      if(InterlockedDecrement(&_current) == 0L) {
         _event.set();
      }
   }

   // Increments the event counter.
   void add_count() {
      if(InterlockedIncrement(&_current) == 1L) {
         _event.reset();
      }
   }
   
   // Blocks the current context until the event is set.
   void wait() {
      _event.wait();
   }
 
private:
   // The current count.
   volatile long _current;
   // The event that is set when the counter reaches zero.
   event _event;

   // Disable copy constructor.
   countdown_event(const countdown_event&);
   // Disable assignment.
   countdown_event const & operator=(countdown_event const&);
};

// A basic agent that resembles control_flow_agent, but uses uses dataflow to 
// perform computations when data becomes available.
class dataflow_agent : public agent
{
public:
   dataflow_agent(ISource<int>& source)
      : _source(source)
   {
   }

   // Retrieves the count of negative numbers that the agent received.
   size_t negatives() 
   {
      return receive(_negatives);
   }

   // Retrieves the count of positive numbers that the agent received.
   size_t positives()
   {
      return receive(_positives);
   }

protected:
   void run()
   {
      // Counts the number of negative and positive values that
      // the agent receives.
      size_t negative_count = 0;
      size_t positive_count = 0;

      // Tracks the count of active operations.
      countdown_event active;
      // An event that is set by the sentinel.
      event received_sentinel;
      
      //
      // Create the members of the dataflow network.
      //
     
      // Increments the active counter.
      transformer<int, int> increment_active(
         [&active](int value) -> int {
            active.add_count();
            return value;
         });

      // Increments the count of negative values.
      call<int> negatives(
         [&](int value) {
            ++negative_count;
            // Decrement the active counter.
            active.signal();
         },
         [](int value) -> bool {
            return value < 0;
         });

      // Increments the count of positive values.
      call<int> positives(
         [&](int value) {
            ++positive_count;
            // Decrement the active counter.
            active.signal();
         },
         [](int value) -> bool {
            return value > 0;
         });

      // Receives only the sentinel value of 0.
      call<int> sentinel(
         [&](int value) {            
            // Decrement the active counter.
            active.signal();
            // Set the sentinel event.
            received_sentinel.set();
         },
         [](int value) { 
            return value == 0; 
         });

      // Connects the _source message buffer to the rest of the network.
      unbounded_buffer<int> connector;
       
      //
      // Connect the network.
      //

      // Connect the internal nodes of the network.
      connector.link_target(&negatives);
      connector.link_target(&positives);
      connector.link_target(&sentinel);
      increment_active.link_target(&connector);

      // Connect the _source buffer to the internal network to 
      // begin data flow.
      _source.link_target(&increment_active);

      // Wait for the sentinel event and for all operations to finish.
      received_sentinel.wait();
      active.wait();
           
      // Write the counts to the message buffers.
      send(_negatives, negative_count);
      send(_positives, positive_count);

      // Set the agent to the completed state.
      done();
   }

private:
   // Source message buffer to read from.
   ISource<int>& _source;
   
   // Holds the number of negative and positive numbers that the agent receives.
   single_assignment<size_t> _negatives;
   single_assignment<size_t> _positives;
};

// Sends a number of random values to the provided message buffer.
void send_values(ITarget<int>& source, int sentinel, size_t count)
{
   // Send a series of random numbers to the source buffer.
   mt19937 rnd(42);
   for (size_t i = 0; i < count; ++i)
   {
      // Generate a random number that is not equal to the sentinel value.
      int n;
      while ((n = rnd()) == sentinel);

      send(source, n);      
   }
   // Send the sentinel value.
   send(source, sentinel);   
}

int wmain()
{
   // Signals to the agent that there are no more values to process.
   const int sentinel = 0;
   // The number of samples to send to each agent.
   const size_t count = 1000000;

   // The source buffer that the application writes numbers to and 
   // the agents read numbers from.
   unbounded_buffer<int> source;

   //
   // Use a control-flow agent to process a series of random numbers.
   //
   wcout << L"Control-flow agent:" << endl;

   // Create and start the agent.
   control_flow_agent cf_agent(source);
   cf_agent.start();
   
   // Send values to the agent.
   send_values(source, sentinel, count);
   
   // Wait for the agent to finish.
   agent::wait(&cf_agent);
   
   // Print the count of negative and positive numbers.
   wcout << L"There are " << cf_agent.negatives() 
         << L" negative numbers."<< endl;
   wcout << L"There are " << cf_agent.positives() 
         << L" positive numbers."<< endl;  

   //
   // Perform the same task, but this time with a dataflow agent.
   //
   wcout << L"Dataflow agent:" << endl;

   // Create and start the agent.
   dataflow_agent df_agent(source);
   df_agent.start();
   
   // Send values to the agent.
   send_values(source, sentinel, count);
   
   // Wait for the agent to finish.
   agent::wait(&df_agent);
   
   // Print the count of negative and positive numbers.
   wcout << L"There are " << df_agent.negatives() 
         << L" negative numbers."<< endl;
   wcout << L"There are " << df_agent.positives() 
         << L" positive numbers."<< endl;
}

이 예제에서는 다음과 같은 샘플 출력을 생성 합니다.This example produces the following sample output:

Control-flow agent:
There are 500523 negative numbers.
There are 499477 positive numbers.
Dataflow agent:
There are 500523 negative numbers.
There are 499477 positive numbers.

코드 컴파일Compiling the Code

예제 코드를 복사 하 여 Visual Studio 프로젝트에 붙여넣거나, dataflow-agent.cpp 이름이 지정 된 파일에 붙여 넣은 후 Visual Studio 명령 프롬프트 창에서 다음 명령을 실행 합니다.Copy the example code and paste it in a Visual Studio project, or paste it in a file that is named dataflow-agent.cpp and then run the following command in a Visual Studio Command Prompt window.

cl.exe/EHsc dataflow-agentcl.exe /EHsc dataflow-agent.cpp

[맨 위로 이동][Top]

메시지 로깅 에이전트 만들기Creating a Message-Logging Agent

다음 예제에서는 dataflow_agent 클래스와 유사한 log_agent 클래스를 보여 줍니다.The following example shows the log_agent class, which resembles the dataflow_agent class. log_agent 클래스는 로그 메시지를 파일 및 콘솔에 기록 하는 비동기 로깅 에이전트를 구현 합니다.The log_agent class implements an asynchronous logging agent that writes log messages to a file and to the console. log_agent 클래스를 사용 하면 응용 프로그램에서 메시지를 정보, 경고 또는 오류로 분류할 수 있습니다.The log_agent class enables the application to categorize messages as informational, warning, or error. 또한 응용 프로그램에서 각 로그 범주를 파일, 콘솔 또는 둘 다에 쓸지 여부를 지정할 수 있습니다.It also enables the application to specify whether each log category is written to a file, the console, or both. 이 예제에서는 모든 로그 메시지를 파일에 기록 하 고 오류 메시지만 콘솔에 기록 합니다.This example writes all log messages to a file and only error messages to the console.

// log-filter.cpp
// compile with: /EHsc 
#include <windows.h>
#include <agents.h>
#include <sstream>
#include <fstream>
#include <iostream>

using namespace concurrency;
using namespace std;

// A synchronization primitive that is signaled when its 
// count reaches zero.
class countdown_event
{
public:
    countdown_event(unsigned int count = 0L)
        : _current(static_cast<long>(count)) 
    {
        // Set the event if the initial count is zero.
        if (_current == 0L)
        {
            _event.set();
        }
    }

    // Decrements the event counter.
    void signal()
    {
        if(InterlockedDecrement(&_current) == 0L)
        {
            _event.set();
        }
    }

    // Increments the event counter.
    void add_count()
    {
        if(InterlockedIncrement(&_current) == 1L)
        {
            _event.reset();
        }
    }

    // Blocks the current context until the event is set.
    void wait()
    {
        _event.wait();
    }

private:
    // The current count.
    volatile long _current;
    // The event that is set when the counter reaches zero.
    event _event;

    // Disable copy constructor.
    countdown_event(const countdown_event&);
    // Disable assignment.
    countdown_event const & operator=(countdown_event const&);
};

// Defines message types for the logger.
enum log_message_type
{
    log_info    = 0x1,
    log_warning = 0x2,
    log_error   = 0x4,
};

// An asynchronous logging agent that writes log messages to 
// file and to the console.
class log_agent : public agent
{
    // Holds a message string and its logging type.
    struct log_message
    {
        wstring message;
        log_message_type type;
    };

public:
    log_agent(const wstring& file_path, log_message_type file_messages, log_message_type console_messages)
        : _file(file_path)
        , _file_messages(file_messages)
        , _console_messages(console_messages)
        , _active(0)
    {
        if (_file.bad())
        {
            throw invalid_argument("Unable to open log file.");
        }
    }

    // Writes the provided message to the log.
    void log(const wstring& message, log_message_type type)
    {  
        // Increment the active message count.
        _active.add_count();

        // Send the message to the network.
        log_message msg = { message, type };
        send(_log_buffer, msg);
    }

    void close()
    {
        // Signal that the agent is now closed.
        _closed.set();
    }

protected:

    void run()
    {
        //
        // Create the dataflow network.
        //

        // Writes a log message to file.
        call<log_message> writer([this](log_message msg)
        {
            if ((msg.type & _file_messages) != 0)
            {
                // Write the message to the file.
                write_to_stream(msg, _file);
            }
            if ((msg.type & _console_messages) != 0)
            {
                // Write the message to the console.
                write_to_stream(msg, wcout);
            }
            // Decrement the active counter.
            _active.signal();
        });

        // Connect _log_buffer to the internal network to begin data flow.
        _log_buffer.link_target(&writer);

        // Wait for the closed event to be signaled.
        _closed.wait();

        // Wait for all messages to be processed.
        _active.wait();

        // Close the log file and flush the console.
        _file.close();
        wcout.flush();

        // Set the agent to the completed state.
        done();
    }

private:
    // Writes a logging message to the specified output stream.
    void write_to_stream(const log_message& msg, wostream& stream)
    {
        // Write the message to the stream.
        wstringstream ss;

        switch (msg.type)
        {
        case log_info:
            ss << L"info: ";
            break;
        case log_warning:
            ss << L"warning: ";
            break;
        case log_error:
            ss << L"error: ";
        }

        ss << msg.message << endl;
        stream << ss.str();
    }

private:   
    // The file stream to write messages to.
    wofstream _file;

    // The log message types that are written to file.
    log_message_type _file_messages;

    // The log message types that are written to the console.
    log_message_type _console_messages;

    // The head of the network. Propagates logging messages
    // to the rest of the network.
    unbounded_buffer<log_message> _log_buffer;

    // Counts the number of active messages in the network.
    countdown_event _active;

    // Signals that the agent has been closed.
    event _closed;
};

int wmain()
{
    // Union of all log message types.
    log_message_type log_all = log_message_type(log_info | log_warning  | log_error);

    // Create a logging agent that writes all log messages to file and error 
    // messages to the console.
    log_agent logger(L"log.txt", log_all, log_error);

    // Start the agent.
    logger.start();

    // Log a few messages.

    logger.log(L"===Logging started.===", log_info);

    logger.log(L"This is a sample warning message.", log_warning);
    logger.log(L"This is a sample error message.", log_error);

    logger.log(L"===Logging finished.===", log_info);

    // Close the logger and wait for the agent to finish.
    logger.close();
    agent::wait(&logger);
}

이 예제에서는 콘솔에 다음 출력을 기록 합니다.This example writes the following output to the console.

error: This is a sample error message.

또한이 예제에서는 다음 텍스트를 포함 하는 .log 파일을 생성 합니다.This example also produces the log.txt file, which contains the following text.

info: ===Logging started.===
warning: This is a sample warning message.
error: This is a sample error message.
info: ===Logging finished.===

코드 컴파일Compiling the Code

예제 코드를 복사 하 여 Visual Studio 프로젝트에 붙여넣거나, log-filter.cpp 이름이 지정 된 파일에 붙여 넣은 후 Visual Studio 명령 프롬프트 창에서 다음 명령을 실행 합니다.Copy the example code and paste it in a Visual Studio project, or paste it in a file that is named log-filter.cpp and then run the following command in a Visual Studio Command Prompt window.

cl.exe/EHsc log-filtercl.exe /EHsc log-filter.cpp

[맨 위로 이동][Top]

참고 항목See also

동시성 런타임 연습Concurrency Runtime Walkthroughs