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

비동기 에이전트 라이브러리(또는 에이전트 라이브러리)는 동시성 지원 애플리케이션 개발의 견고성을 높일 수 있는 프로그래밍 모델을 제공합니다. 에이전트 라이브러리는 거친 데이터 흐름 및 파이프라인 작업을 위해 행위자 기반 프로그래밍 모델 및 In-Process 메시지를 전달하는 C++ 템플릿 라이브러리입니다. 에이전트 라이브러리는 동시성 런타임의 일정 및 리소스 관리 구성 요소를 기반으로 합니다.

프로그래밍 모델

에이전트 라이브러리는 제어 흐름 대신 데이터 흐름을 기반으로 하는 비동기 통신 모델을 통해 격리된 구성 요소를 연결할 수 있도록 하여 공유 상태에 대한 대안을 제공합니다. 데이터 흐름 은 모든 필수 데이터를 사용할 수 있을 때 계산이 수행되는 프로그래밍 모델을 나타냅니다.제어 흐름 은 계산이 미리 결정된 순서로 이루어지는 프로그래밍 모델을 나타냅니다.

데이터 흐름 프로그래밍 모델은 프로그램의 개별 구성 요소가 메시지를 전달하여 서로 통신하는 메시지 전달 개념과 관련됩니다.

에이전트 라이브러리는 비동기 에이전트, 비동기 메시지 블록메시지 전달 함수의 세 가지 구성 요소로 구성됩니다. 에이전트는 상태를 기본 메시지 블록 및 메시지 전달 함수를 사용하여 서로 외부 구성 요소와 통신합니다. 메시지 전달 함수를 사용하면 에이전트가 외부 구성 요소에 메시지를 보내고 받을 수 있습니다. 비동기 메시지 블록은 메시지를 저장하고 에이전트가 동기화된 방식으로 통신할 수 있도록 합니다.

다음 그림에서는 두 에이전트가 메시지 블록과 메시지 전달 함수를 사용하여 통신하는 방법을 보여 줍니다. 이 그림 agent1 에서는 동시성::send 함수 및 동시성::unbounded_buffer 개체를 사용하여 메시지를 agent2 보냅니다. agent2동시성::receive 함수를 사용하여 메시지를 읽습니다. agent2 는 동일한 메서드를 사용하여 메시지를 agent1보냅니다. 파선 화살표는 에이전트 간의 데이터 흐름을 나타냅니다. 단색 화살표는 에이전트가 쓰거나 읽은 메시지 블록에 연결합니다.

The components of the Agents Library.

이 그림을 구현하는 코드 예제는 이 항목의 뒷부분에 나와 있습니다.

에이전트 프로그래밍 모델은 이벤트와 같은 다른 동시성 및 동기화 메커니즘에 비해 몇 가지 이점이 있습니다. 한 가지 장점은 메시지 전달을 사용하여 개체 간에 상태 변경 내용을 전송함으로써 공유 리소스에 대한 액세스를 격리하여 확장성을 향상시킬 수 있다는 것입니다. 메시지 전달의 장점은 동기화를 외부 동기화 개체에 연결하는 대신 데이터와 연결한다는 것입니다. 이렇게 하면 구성 요소 간의 데이터 전송이 간소화되고 애플리케이션에서 프로그래밍 오류가 제거됩니다.

에이전트 라이브러리를 사용하는 경우

서로 비동기적으로 통신해야 하는 여러 작업이 있는 경우 에이전트 라이브러리를 사용합니다. 메시지 블록 및 메시지 전달 함수를 사용하면 잠금과 같은 동기화 메커니즘 없이 병렬 애플리케이션을 작성할 수 있습니다. 이렇게 하면 애플리케이션 논리에 집중할 수 있습니다.

에이전트 프로그래밍 모델은 데이터 파이프라인 또는 네트워크를 만드는 데 자주 사용됩니다. 데이터 파이프라인은 각각 더 큰 목표에 기여하는 특정 작업을 수행하는 일련의 구성 요소입니다. 데이터 흐름 파이프라인의 모든 구성 요소는 다른 구성 요소에서 메시지를 받을 때 작업을 수행합니다. 해당 작업의 결과는 파이프라인 또는 네트워크의 다른 구성 요소에 전달됩니다. 구성 요소는 다른 라이브러리(예 : PPL(병렬 패턴 라이브러리))의 보다 세분화된 동시성 기능을 사용할 수 있습니다.

예시

다음 예제에서는 이 항목의 앞부분에 표시된 그림을 구현합니다.

// basic-agents.cpp
// compile with: /EHsc
#include <agents.h>
#include <string>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// This agent writes a string to its target and reads an integer
// from its source.
class agent1 : public agent 
{
public:
   explicit agent1(ISource<int>& source, ITarget<wstring>& target)
      : _source(source)
      , _target(target)
   {
   }

protected:
   void run()
   {
      // Send the request.
      wstringstream ss;
      ss << L"agent1: sending request..." << endl;
      wcout << ss.str();

      send(_target, wstring(L"request"));

      // Read the response.
      int response = receive(_source);

      ss = wstringstream();
      ss << L"agent1: received '" << response << L"'." << endl;
      wcout << ss.str();

      // Move the agent to the finished state.
      done();
   }

private:   
   ISource<int>& _source;
   ITarget<wstring>& _target;
};

// This agent reads a string to its source and then writes an integer
// to its target.
class agent2 : public agent 
{
public:
   explicit agent2(ISource<wstring>& source, ITarget<int>& target)
      : _source(source)
      , _target(target)
   {
   }

protected:
   void run()
   {
      // Read the request.
      wstring request = receive(_source);

      wstringstream ss;
      ss << L"agent2: received '" << request << L"'." << endl;
      wcout << ss.str();

      // Send the response.
      ss = wstringstream();
      ss << L"agent2: sending response..." << endl;
      wcout << ss.str();

      send(_target, 42);

      // Move the agent to the finished state.
      done();
   }

private:   
   ISource<wstring>& _source;
   ITarget<int>& _target;
};

int wmain()
{
   // Step 1: Create two message buffers to serve as communication channels
   // between the agents.
   
   // The first agent writes messages to this buffer; the second
   // agents reads messages from this buffer.
   unbounded_buffer<wstring> buffer1;

   // The first agent reads messages from this buffer; the second
   // agents writes messages to this buffer.
   overwrite_buffer<int> buffer2;

   // Step 2: Create the agents.
   agent1 first_agent(buffer2, buffer1);
   agent2 second_agent(buffer1, buffer2);

   // Step 3: Start the agents. The runtime calls the run method on
   // each agent.
   first_agent.start();
   second_agent.start();

   // Step 4: Wait for both agents to finish.
   agent::wait(&first_agent);
   agent::wait(&second_agent);
}

이 예제는 다음과 같은 출력을 생성합니다.

agent1: sending request...
agent2: received 'request'.
agent2: sending response...
agent1: received '42'.

다음 항목에서는 이 예제에서 사용되는 기능에 대해 설명합니다.

비동기 에이전트
더 큰 컴퓨팅 작업을 해결하는 비동기 에이전트의 역할을 설명합니다.

비동기 메시지 블록
에이전트 라이브러리에서 제공하는 다양한 메시지 블록 형식에 대해 설명합니다.

메시지 전달 함수
에이전트 라이브러리에서 제공하는 다양한 메시지 전달 루틴에 대해 설명합니다.

방법: 다양한 공급자-소비자 패턴 구현
애플리케이션에서 생산자-소비자 패턴을 구현하는 방법을 설명합니다.

방법: call 및 transformer 클래스에 작업 함수 제공
동시성::call동시성::변환기 클래스에 작업 함수를 제공하는 여러 가지 방법을 보여 줍니다.

방법: 데이터 파이프라인에서 transformer 사용
데이터 파이프라인에서 동시성::변환기 클래스를 사용하는 방법을 보여줍니다.

방법: 완료된 작업 간 선택
동시성::선택동시성::조인 클래스를 사용하여 검색 알고리즘을 완료하는 첫 번째 작업을 선택하는 방법을 보여 줍니다.

방법: 정기적으로 메시지 보내기
concurrency::timer 클래스를 사용하여 정기적으로 메시지를 보내는 방법을 보여줍니다.

방법: 메시지 블록 필터 사용
필터를 사용하여 비동기 메시지 블록이 메시지를 수락하거나 거부할 수 있도록 하는 방법을 보여 줍니다.

PPL(병렬 패턴 라이브러리)
애플리케이션에서 병렬 알고리즘과 같은 다양한 병렬 패턴을 사용하는 방법을 설명합니다.

동시성 런타임
병렬 프로그래밍을 간소화하는 동시성 런타임에 대해 설명하고 관련 항목의 링크를 제공합니다.