다음을 통해 공유


비동기 메시지 블록

업데이트: 2010년 8월

에이전트 라이브러리에서는 스레드로부터 안전한 방식으로 응용 프로그램 구성 요소 간에 메시지를 전파할 수 있도록 하는 몇 가지 메시지 블록 형식을 제공합니다. 이러한 메시지 블록 형식은 주로 Concurrency::send, Concurrency::asend, Concurrency::receiveConcurrency::try_receive와 같은 다양한 메시지 전달 루틴에 사용됩니다. 에이전트 라이브러리에 정의된 메시지 전달 루틴에 대한 자세한 내용은 메시지 전달 함수를 참조하십시오.

단원

이 항목에는 다음과 같은 단원이 포함되어 있습니다.

  • 소스 및 대상

  • 메시지 전파

  • 메시지 블록 형식 개요

  • unbounded_buffer 클래스

  • overwrite_buffer 클래스

  • single_assignment 클래스

  • call 클래스

  • transformer 클래스

  • choice 클래스

  • join 및 multitype_join 클래스

  • timer 클래스

  • 메시지 필터링

  • 메시지 예약

소스 및 대상

소스와 대상은 메시지 전달의 중요한 두 참가 요소입니다. 소스는 메시지를 보내는 통신의 끝점을 나타내고 대상은 메시지를 받는 통신의 끝점을 나타냅니다. 소스는 메시지를 읽는 끝점으로 간주하고 대상은 메시지를 작성하는 끝점으로 간주할 수 있습니다. 응용 프로그램에서는 소스와 대상을 함께 연결하여 메시징 네트워크를 구성합니다.

에이전트 라이브러리에서는 Concurrency::ISourceConcurrency::ITarget이라는 두 개의 추상 클래스를 사용하여 소스와 대상을 나타냅니다. 소스로 사용되는 메시지 블록 형식은 ISource에서 파생되고 대상으로 사용되는 메시지 블록 형식은 ITarget에서 파생됩니다. 소스와 대상으로 사용되는 메시지 블록 형식은 ISourceITarget에서 파생됩니다.

[맨 위로 이동]

메시지 전파

메시지 전파는 구성 요소 간에 메시지를 보내는 동작입니다. 메시지 블록은 제공된 메시지를 수락하거나 거부하거나 연기할 수 있습니다. 모든 메시지 블록 형식은 다양한 방식으로 메시지를 저장하고 전송합니다. 예를 들어 unbounded_buffer 클래스는 개수에 관계없이 메시지를 저장하고 overwrite_buffer 클래스는 한 번에 하나의 메시지를 저장하며 변환기 클래스는 각 메시지의 변경된 버전을 저장합니다. 이러한 메시지 블록 형식에 대해서는 이 문서의 뒷부분에서 자세히 설명합니다.

메시지 블록에서 메시지를 수락하면 선택적으로 작업을 수행할 수 있으며 메시지 블록이 소스인 경우 결과 메시지를 네트워크의 다른 멤버에게 전달합니다. 메시지 블록은 필터 함수를 사용하여 받지 않을 메시지를 거부할 수 있습니다. 필터에 대한 내용은 이 항목 뒷부분의 메시지 필터링 단원에서 자세히 설명합니다. 메시지를 연기하는 메시지 블록은 해당 메시지를 예약한 후 나중에 사용할 수 있습니다. 메시지 예약에 대한 내용은 이 항목 뒷부분의 메시지 예약 단원에서 자세히 설명합니다.

에이전트 라이브러리를 사용하면 메시지 블록이 메시지를 동기적 또는 비동기적으로 전달할 수 있습니다. 예를 들어 send 함수를 사용하여 메시지를 동기적으로 메시지 블록에 전달하면 대상 블록이 메시지를 수락하거나 거부할 때까지 런타임에서 현재 컨텍스트를 차단합니다. 예를 들어 asend 함수를 사용하여 메시지를 비동기적으로 메시지 블록에 전달하면 런타임에서 대상에 메시지를 제공하며 대상이 메시지를 수락할 경우 런타임에서 수신자에게 메시지를 전파하는 비동기 작업을 예약합니다. 런타임에서는 간단한 작업을 사용하여 협조적 방식으로 메시지를 전파합니다. 간단한 작업에 대한 자세한 내용은 작업 스케줄러(동시성 런타임)를 참조하십시오.

응용 프로그램에서는 소스와 대상을 함께 연결하여 메시징 네트워크를 구성합니다. 일반적으로 네트워크를 연결하고 send 또는 asend를 호출하여 데이터를 네트워크에 전달합니다. 소스 메시지 블록을 대상에 연결하려면 Concurrency::ISource::link_target 메서드를 호출합니다. 특정 대상에서 소스 블록의 연결을 끊으려면 Concurrency::ISource::unlink_target 메서드를 호출합니다. 모든 해당 대상에서 소스 블록의 연결을 끊으려면 Concurrency::ISource::unlink_targets 메서드를 호출합니다. 미리 정의된 메시지 블록 형식 중 하나가 범위를 벗어나거나 삭제되면 자동으로 모든 대상 블록과 연결이 끊어집니다. 일부 메시지 블록 형식은 쓸 수 있는 최대 대상 개수를 제한합니다. 다음 단원에서는 미리 정의된 메시지 블록 형식에 적용되는 제한 사항에 대해 설명합니다.

[맨 위로 이동]

메시지 블록 형식 개요

다음 표에서는 중요한 메시지 블록 형식의 역할에 대해 간략하게 설명합니다.

  • unbounded_buffer
    메시지 큐를 저장합니다.

  • overwrite_buffer
    여러 번 읽고 쓸 수 있는 하나의 메시지를 저장합니다.

  • single_assignment
    한 번 쓸 수 있고 여러 번 읽을 수 있는 하나의 메시지를 저장합니다.

  • call
    메시지를 받으면 작업을 수행합니다.

  • transformer
    데이터를 받으면 작업을 수행하고 해당 작업의 결과를 다른 대상 블록에 보냅니다. transformer 클래스는 다른 입력 및 출력 형식에서 동작할 수 있습니다.

  • choice
    소스 집합에서 사용 가능한 첫 번째 메시지를 선택합니다.

  • join 및 multitype join
    소스 집합에서 모든 메시지를 받을 때까지 기다린 다음, 메시지를 다른 메시지 블록에 대한 하나의 메시지로 결합합니다.

  • timer
    메시지를 정기적으로 대상 블록에 보냅니다.

이러한 메시지 블록 형식에는 상황에 따라 유용하게 적용할 수 있는 다양한 특성이 있습니다. 다음은 이러한 특성 중 일부입니다.

  • 전파 형식: 메시지 블록을 데이터 소스로 사용할지, 데이터 수신자로 사용할지 아니면 둘 다로 사용할지를 지정합니다.

  • 메시지 순서 지정: 메시지 블록에서 메시지를 보내고 받는 원래 순서를 그대로 유지할지 여부를 지정합니다. 미리 정의된 각 메시지 블록 형식은 메시지를 보내고 받는 원래 순서를 유지합니다.

  • 소스 개수: 메시지 블록에서 읽을 수 있는 최대 소스 개수입니다.

  • 대상 개수: 메시지 블록에서 쓸 수 있는 최대 대상 개수입니다.

다음 표에서는 이러한 특성과 다양한 메시지 블록 형식의 관계를 보여 줍니다.

메시지 블록 형식

전파 형식(소스, 대상 또는 둘 다)

메시지 순서 지정(순서 지정됨 또는 순서 지정 안 됨)

소스 개수

대상 개수

unbounded_buffer

모두

순서 지정됨

제한 없음

제한 없음

overwrite_buffer

모두

순서 지정됨

제한 없음

제한 없음

single_assignment

모두

순서 지정됨

제한 없음

제한 없음

call

대상

순서 지정됨

제한 없음

적용할 수 없음

transformer

모두

순서 지정됨

제한 없음

1

choice

모두

순서 지정됨

10

1

join

모두

순서 지정됨

제한 없음

1

multitype_join

모두

순서 지정됨

10

1

timer

소스

적용할 수 없음

적용할 수 없음

1

다음 단원에서는 메시지 블록 형식에 대해 자세히 설명합니다.

[맨 위로 이동]

unbounded_buffer 클래스

Concurrency::unbounded_buffer 클래스는 일반적으로 사용되는 비동기 메시징 구조입니다. 이 클래스는 여러 소스에서 쓸 수 있거나 여러 대상에서 읽을 수 있는 FIFO(선입선출) 메시지 큐를 저장합니다. 대상이 unbounded_buffer 개체에서 메시지를 받으면 해당 메시지가 메시지 큐에서 제거됩니다. 따라서 unbounded_buffer 개체에 대상이 여러 개 있더라도 각 메시지를 받을 대상은 하나뿐입니다. unbounded_buffer 클래스는 여러 메시지를 다른 구성 요소에 전달하고 해당 구성 요소가 각 메시지를 받아야 하는 경우에 유용합니다.

예제

다음 예제에서는 unbounded_buffer 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 이 예제에서는 세 개의 값을 unbounded_buffer 개체에 보낸 다음 다시 같은 개체에서 해당 값을 읽습니다.

// unbounded_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an unbounded_buffer object that works with
   // int data.
   unbounded_buffer<int> items;

   // Send a few items to the unbounded_buffer object.
   send(items, 33);
   send(items, 44);
   send(items, 55);

   // Read the items from the unbounded_buffer object and print
   // them to the console.
   wcout << receive(items) << endl;
   wcout << receive(items) << endl;
   wcout << receive(items) << endl;
}

이 예제의 결과는 다음과 같습니다.

33
44
55

unbounded_buffer 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: 다양한 공급자/소비자 패턴 구현을 참조하십시오.

[맨 위로 이동]

overwrite_buffer 클래스

Concurrency::overwrite_buffer 클래스는 unbounded_buffer 클래스와 비슷하지만 overwrite_buffer 개체는 메시지를 하나만 저장합니다. 또한 대상이 overwrite_buffer 개체에서 메시지를 받으면 해당 메시지가 버퍼에서 제거되지 않습니다. 따라서 여러 대상에 메시지의 복사본이 전달됩니다.

overwrite_buffer 클래스는 여러 메시지를 다른 구성 요소에 전달하지만 해당 구성 요소에 최신 값만 필요한 경우에 유용합니다. 또한 이 클래스는 메시지를 여러 구성 요소에 브로드캐스팅하려는 경우에도 유용합니다.

예제

다음 예제에서는 overwrite_buffer 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 이 예제에서는 세 개의 값을 overwrite _buffer 개체에 보낸 다음 같은 개체에서 현재 값을 세 번 읽습니다. 이 예제는 unbounded_buffer 클래스에 대한 예제와 비슷합니다. 그러나 overwrite_buffer 클래스는 메시지를 하나만 저장합니다. 또한 런타임에서 읽은 메시지를 overwrite_buffer 개체에서 제거하지 않습니다.

// overwrite_buffer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an overwrite_buffer object that works with
   // int data.
   overwrite_buffer<int> item;

   // Send a few items to the overwrite_buffer object.
   send(item, 33);
   send(item, 44);
   send(item, 55);

   // Read the current item from the overwrite_buffer object and print
   // it to the console three times.
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
}

이 예제의 결과는 다음과 같습니다.

55
55
55

overwrite_buffer 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: 다양한 공급자/소비자 패턴 구현을 참조하십시오.

[맨 위로 이동]

single_assignment 클래스

Concurrency::single_assignment 클래스는 overwrite_buffer 클래스와 비슷하지만 single_assignment 개체는 한 번만 쓸 수 있습니다. overwrite_buffer 클래스와 마찬가지로 대상이 single_assignment 개체에서 메시지를 받으면 해당 메시지가 이 개체에서 제거되지 않습니다. 따라서 여러 대상에 메시지의 복사본이 전달됩니다. single_assignment 클래스는 하나의 메시지를 여러 구성 요소에 브로드캐스팅하려는 경우에 유용합니다.

예제

다음 예제에서는 single_assignment 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 이 예제에서는 세 개의 값을 single_assignment 개체에 보낸 다음 같은 개체에서 현재 값을 세 번 읽습니다. 이 예제는 overwrite_buffer 클래스에 대한 예제와 비슷합니다. overwrite_buffersingle_assignment 클래스 둘 다 단일 메시지를 저장하지만 single_assignment 클래스는 한 번만 쓸 수 있습니다.

// single_assignment-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an single_assignment object that works with
   // int data.
   single_assignment<int> item;

   // Send a few items to the single_assignment object.
   send(item, 33);
   send(item, 44);
   send(item, 55);

   // Read the current item from the single_assignment object and print
   // it to the console three times.
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
   wcout << receive(item) << endl;
}

이 예제의 결과는 다음과 같습니다.

33
33
33

single_assignment 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 연습: 미래 구현을 참조하십시오.

[맨 위로 이동]

call 클래스

Concurrency::call 클래스는 데이터를 받으면 작업 함수를 수행하는 메시지 수신자로 사용됩니다. 이 작업 함수는 람다 식, 함수 개체 또는 함수 포인터가 될 수 있습니다. call 개체는 메시지를 보내는 다른 구성 요소와 나란히 동작하므로 일반적인 함수 호출과는 동작 방식이 다릅니다. If a call 개체는 메시지를 받고 작업을 수행 중인 경우 해당 메시지를 큐에 추가합니다. 모든 call 개체는 큐에 대기 중인 메시지를 받은 순서대로 처리합니다.

예제

다음 예제에서는 call 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 이 예제에서는 받는 각각의 값을 콘솔에 출력하는 call 개체를 만듭니다. 그런 다음 call 개체에 세 개의 값을 보냅니다. call 개체는 별도의 스레드에서 메시지를 처리하므로 이 예제에서는 카운터 변수와 event 개체를 사용하여 wmain 함수가 반환하기 전에 call 개체가 모든 메시지를 처리하도록 합니다.

// call-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // An event that is set when the call object receives all values.
   event received_all;

   // Counts the 
   long receive_count = 0L;
   long max_receive_count = 3L;

   // Create an call object that works with int data.
   call<int> target([&received_all,&receive_count,max_receive_count](int n) {
      // Print the value that the call object receives to the console.
      wcout << n << endl;

      // Set the event when all messages have been processed.
      if (++receive_count == max_receive_count)
         received_all.set();
   });

   // Send a few items to the call object.
   send(target, 33);
   send(target, 44);
   send(target, 55);

   // Wait for the call object to process all items.
   received_all.wait();
}

이 예제의 결과는 다음과 같습니다.

33
44
55

call 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: call 및 transformer 클래스에 작업 함수 제공을 참조하십시오.

[맨 위로 이동]

transformer 클래스

Concurrency::transformer 클래스는 메시지 수신자와 메시지 전송자 역할을 둘 다 합니다. transformer 클래스는 데이터를 받으면 사용자 정의 작업 함수를 수행하므로 call 클래스와 비슷합니다. 그러나 transformer 클래스는 작업 함수의 결과도 수신자 개체에 보냅니다. call 개체와 마찬가지로 transformer 개체는 메시지를 보내는 다른 구성 요소와 나란히 동작합니다. transformer 개체는 메시지를 받고 작업을 수행 중인 경우 해당 메시지를 큐에 추가합니다. 모든 transformer 개체는 큐에 대기 중인 메시지를 받은 순서대로 처리합니다.

transformer 클래스는 메시지를 하나의 대상에 보냅니다. 생성자에서 _PTarget 매개 변수를 NULL로 설정하면 Concurrency::link_target 메서드를 호출하여 나중에 대상을 지정할 수 있습니다.

에이전트 라이브러리에서 제공하는 다른 모든 비동기 메시지 블록 형식과 달리 transformer 클래스는 다른 입력 및 출력 형식에서 동작할 수 있습니다. 한 형식에서 다른 형식으로 데이터를 전송할 수 있는 기능으로 인해 transformer 클래스는 많은 동시 네트워크에서 핵심 구성 요소로 사용됩니다. 또한 transformer 개체의 작업 함수에서 더 세분화된 병렬 기능을 추가할 수 있습니다.

예제

다음 예제에서는 transformer 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 이 예제에서는 double 값을 출력으로 생성하기 위해 각 입력 int 값에 0.33을 곱하는 transformer 개체를 만듭니다. 그런 다음 같은 transformer 개체에서 변환된 값을 받고 해당 값을 콘솔에 출력합니다.

// transformer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an transformer object that receives int data and 
   // sends double data.
   transformer<int, double> third([](int n) {
      // Return one-third of the input value.
      return n * 0.33;
   });

   // Send a few items to the transformer object.
   send(third, 33);
   send(third, 44);
   send(third, 55);

   // Read the processed items from the transformer object and print
   // them to the console.
   wcout << receive(third) << endl;
   wcout << receive(third) << endl;
   wcout << receive(third) << endl;
}

이 예제의 결과는 다음과 같습니다.

10.89
14.52
18.15

transformer 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: 데이터 파이프라인에서 transformer 사용을 참조하십시오.

[맨 위로 이동]

choice 클래스

Concurrency::choice 클래스는 소스 집합에서 사용 가능한 첫 번째 메시지를 선택합니다. choice 클래스는 데이터 흐름 메커니즘 대신 제어 흐름 메커니즘을 나타냅니다. 비동기 에이전트 라이브러리 항목에서는 데이터 흐름과 제어 흐름의 차이점에 대해 설명합니다.

choice 개체를 읽는 것은 bWaitAll 매개 변수가 FALSE로 설정된 Windows API WaitForMultipleObjects 함수를 호출하는 것과 비슷합니다. 그러나 choice 클래스는 외부 동기화 개체 대신 이벤트 자체에 데이터를 바인딩합니다.

일반적으로 choice 클래스를 Concurrency::receive 함수와 함께 사용하여 응용 프로그램에서 제어 흐름을 구동합니다. 다양한 형식을 포함하는 메시지 버퍼에서 선택해야 하는 경우 choice 클래스를 사용하고, 같은 형식을 포함하는 메시지 버퍼에서 선택해야 할 경우에는 single_assignment 클래스를 사용합니다.

소스를 choice 개체에 연결하는 순서는 선택되는 메시지를 결정할 수 있으므로 중요합니다. 예를 들어 메시지가 이미 포함되어 있는 여러 메시지 버퍼를 choice 개체에 연결하는 경우를 가정해 보십시오. choice 개체는 첫 번째로 연결되는 소스에서 메시지를 선택합니다. 모든 소스를 연결하고 나면 choice 개체는 각 소스에 메시지가 전달되는 순서를 유지합니다.

예제

다음 예제에서는 choice 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 이 예제에서는 Concurrency::make_choice 함수를 사용하여 세 개의 메시지 블록 중에서 선택하는 choice 개체를 만듭니다. 그런 다음 다양한 피보나치 수(Fibonacci number)를 계산하고 각 결과를 다른 메시지 블록에 저장합니다. 그리고 나서 처음에 완료한 작업을 기반으로 하는 메시지를 콘솔에 출력합니다.

// choice-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Although the following thee message blocks are written to one time only, 
   // this example illustrates the fact that the choice class works with 
   // different message block types.

   // Holds the 35th Fibonacci number.
   single_assignment<int> fib35;
   // Holds the 37th Fibonacci number.
   overwrite_buffer<int> fib37;
   // Holds half of the 42nd Fibonacci number.
   unbounded_buffer<double> half_of_fib42;   

   // Create a choice object that selects the first single_assignment 
   // object that receives a value.
   auto select_one = make_choice(&fib35, &fib37, &half_of_fib42);

   // Execute a few lengthy operations in parallel. Each operation sends its 
   // result to one of the single_assignment objects.
   parallel_invoke(
      [&fib35] { send(fib35, fibonacci(35)); },
      [&fib37] { send(fib37, fibonacci(37)); },
      [&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
   );

   // Print a message that is based on the operation that finished first.
   switch (receive(select_one))
   {
   case 0:
      wcout << L"fib35 received its value first. Result = " 
            << receive(fib35) << endl;
      break;
   case 1:
      wcout << L"fib37 received its value first. Result = " 
            << receive(fib37) << endl;
      break;
   case 2:
      wcout << L"half_of_fib42 received its value first. Result = " 
            << receive(half_of_fib42) << endl;
      break;
   default:
      wcout << L"Unexpected." << endl;
      break;
   }
}

이 예제를 실행하면 다음과 같은 샘플 결과가 출력됩니다.

fib35 received its value first. Result = 9227465

35th 피보나치 수(Fibonacci number)를 계산하는 작업이 처음에 완료된다는 보장이 없으므로 이 예제의 출력은 달라질 수 있습니다.

이 예제에서는 Concurrency::parallel_invoke 알고리즘을 사용하여 피보나치 수(Fibonacci number)를 병렬로 계산합니다. parallel_invoke에 대한 자세한 내용은 병렬 알고리즘을 참조하십시오.

choice 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: 완료된 작업 간 선택을 참조하십시오.

[맨 위로 이동]

join 및 multitype_join 클래스

Concurrency::joinConcurrency::multitype_join 클래스를 사용하면 소스 집합의 각 멤버가 메시지를 받을 때까지 기다릴 수 있습니다. join 클래스는 공통 메시지 형식을 포함하는 소스 개체에서 동작하고, multitype_join 클래스는 서로 다른 메시지 형식을 포함할 수 있는 소스 개체에서 동작합니다.

join 또는 multitype_join 개체를 읽는 것은 bWaitAll 매개 변수가 TRUE로 설정된 Windows API WaitForMultipleObjects 함수를 호출하는 것과 비슷합니다. 그러나 choice 개체와 마찬가지로 joinmultitype_join 개체도 외부 동기화 개체 대신 이벤트 자체에 데이터를 바인딩하는 이벤트 메커니즘을 사용합니다.

join 개체를 읽으면 std::vector 개체가 생성되고 multitype_join 개체를 읽으면 std::tuple 개체가 생성됩니다. 이러한 개체에서 해당하는 소스 버퍼와 같은 순서로 나타나는 요소는 join 또는 multitype_join 개체에 연결됩니다. 소스 버퍼를 join 또는 multitype_join 개체에 연결하는 순서가 결과 vector 또는 tuple 개체에서의 요소 순서와 관련되므로 조인에서 기존 소스 버퍼의 연결을 끊지 않는 것이 좋습니다. 이 연결을 끊으면 지정되지 않은 동작이 발생할 수 있습니다.

Greedy 조인 및 Non-Greedy 조인

joinmultitype_join 클래스는 greedy 조인 및 non-greedy 조인 개념을 지원합니다. greedy 조인은 메시지를 사용할 수 있게 되면 모든 메시지를 사용할 수 있을 때까지 각 소스로부터 메시지를 수락합니다. non-greedy 조인은 두 단계로 메시지를 받습니다. 먼저 non-greedy 조인은 각 소스의 메시지가 제공될 때까지 기다립니다. 두 번째로 모든 소스 메시지를 사용할 수 있게 되면 각 메시지를 예약하려고 시도합니다. non-greedy 조인이 각 메시지를 예약할 수 있는 경우 모든 메시지를 사용하고 대상에 전파합니다. 그렇지 않으면 메시지 예약을 해제하거나 취소하고 각 소스가 메시지를 받을 때까지 다시 기다립니다.

Greedy 조인은 메시지를 즉시 수락하므로 non-greedy 조인보다 효율적입니다. 그러나 드문 경우이긴 하지만 greedy 조인을 사용하면 교착 상태가 발생할 수 있습니다. 공유 소스 개체를 하나 이상 포함하는 조인이 여러 개 있는 경우 non-greedy 조인을 사용하십시오.

예제

다음 예제에서는 join 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 이 예제에서는 Concurrency::make_join 함수를 사용하여 세 개의 single_assignment 개체에서 받는 join 개체를 만듭니다. 이 예제에서는 다양한 피보나치 수(Fibonacci number)를 계산하고 각 결과를 다른 single_assignment 개체에 저장한 다음 join 개체에 포함된 각 결과를 콘솔에 출력합니다. join 클래스는 모든 소스 메시지 블록이 메시지를 받을 때까지 기다린다는 점을 제외하면 이 예제는 choice 클래스에 대한 예제와 비슷합니다.

// join-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Holds the 35th Fibonacci number.
   single_assignment<int> fib35;
   // Holds the 37th Fibonacci number.
   single_assignment<int> fib37;
   // Holds half of the 42nd Fibonacci number.
   single_assignment<double> half_of_fib42;   

   // Create a join object that selects the values from each of the
   // single_assignment objects.
   auto join_all = make_join(&fib35, &fib37, &half_of_fib42);

   // Execute a few lengthy operations in parallel. Each operation sends its 
   // result to one of the single_assignment objects.
   parallel_invoke(
      [&fib35] { send(fib35, fibonacci(35)); },
      [&fib37] { send(fib37, fibonacci(37)); },
      [&half_of_fib42] { send(half_of_fib42, fibonacci(42) * 0.5); }
   );

   auto result = receive(join_all);
   wcout << L"fib35 = " << get<0>(result) << endl;
   wcout << L"fib37 = " << get<1>(result) << endl;
   wcout << L"half_of_fib42 = " << get<2>(result) << endl;
}

이 예제의 결과는 다음과 같습니다.

fib35 = 9227465
fib37 = 24157817
half_of_fib42 = 1.33957e+008

이 예제에서는 Concurrency::parallel_invoke 알고리즘을 사용하여 피보나치 수(Fibonacci number)를 병렬로 계산합니다. parallel_invoke에 대한 자세한 내용은 병렬 알고리즘을 참조하십시오.

join 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: 완료된 작업 간 선택연습: join을 사용하여 교착 상태 방지를 참조하십시오.

[맨 위로 이동]

timer 클래스

Concurrency::timer 클래스는 메시지 소스로 사용됩니다. timer 개체는 지정된 시간이 경과하면 대상에 메시지를 보냅니다. timer 클래스는 메시지 보내기를 연기해야 하거나 정기적으로 메시지를 보내려는 경우에 유용합니다.

timer 클래스는 메시지를 하나의 대상에 보냅니다. 생성자에서 _PTarget 매개 변수를 NULL로 설정하면 Concurrency::ISource::link_target 메서드를 호출하여 나중에 대상을 지정할 수 있습니다.

timer 개체는 반복되거나 반복되지 않을 수 있습니다. 반복되는 타이머를 만들려면 생성자를 호출할 때 _Repeating 매개 변수에 true를 전달합니다. 그렇지 않으면 _Repeating 매개 변수에 false를 전달하여 반복되지 않는 타이머를 만듭니다. 반복되는 타이머는 각 간격마다 같은 메시지를 대상에 보냅니다.

에이전트 라이브러리는 시작되지 않은 상태로 timer 개체를 만듭니다. timer 개체를 시작하려면 Concurrency::timer::start 메서드를 호출합니다. timer 개체를 중지하려면 개체를 삭제하거나 Concurrency::timer::stop 메서드를 호출합니다. 반복되는 타이머를 일시 중지하려면 Concurrency::timer::pause 메서드를 호출합니다.

예제

다음 예제에서는 timer 클래스를 사용하는 방법의 기본 구조를 보여 줍니다. 그리고 timercall 개체를 사용하여 시간이 많이 걸리는 작업의 진행률을 보고합니다.

// timer-structure.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
   if (n < 2)
      return n;
   return fibonacci(n-1) + fibonacci(n-2);
}

int wmain()
{
   // Create a call object that prints characters that it receives 
   // to the console.
   call<wchar_t> print_character([](wchar_t c) {
      wcout << c;
   });

   // Create a timer object that sends the period (.) character to 
   // the call object every 100 milliseconds.
   timer<wchar_t> progress_timer(100u, L'.', &print_character, true);

   // Start the timer.
   wcout << L"Computing fib(42)";
   progress_timer.start();

   // Compute the 42nd Fibonacci number.
   int fib42 = fibonacci(42);

   // Stop the timer and print the result.
   progress_timer.stop();
   wcout << endl << L"result is " << fib42 << endl;
}

이 예제를 실행하면 다음과 같은 샘플 결과가 출력됩니다.

Computing fib(42)..................................................
result is 267914296

timer 클래스를 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: 정기적으로 메시지 보내기를 참조하십시오.

[맨 위로 이동]

메시지 필터링

메시지 블록 개체를 만들 때 메시지 블록이 메시지를 수락할지 거부할지를 결정하는 필터 함수를 제공할 수 있습니다. 필터 함수는 메시지 블록이 특정 값만 받을 수 있게 하는 유용한 방법입니다.

다음 예제에서는 필터 함수를 사용하여 짝수만 수락하는 unbounded_buffer 개체를 만드는 방법을 보여 줍니다. unbounded_buffer 개체는 홀수를 거부하므로 홀수는 대상 블록에 전파되지 않습니다.

// filter-function.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace Concurrency;
using namespace std;

int wmain()
{
   // Create an unbounded_buffer object that uses a filter
   // function to accept only even numbers.
   unbounded_buffer<int> accept_evens(
      [](int n) {
         return (n%2) == 0;
      });

   // Send a few values to the unbounded_buffer object.
   unsigned int accept_count = 0;
   for (int i = 0; i < 10; ++i)
   {
      // The asend function returns true only if the target
      // accepts the message. This enables us to determine
      // how many elements are stored in the unbounded_buffer
      // object.
      if (asend(accept_evens, i))
      {
         ++accept_count;
      }
   }

   // Print to the console each value that is stored in the 
   // unbounded_buffer object. The unbounded_buffer object should
   // contain only even numbers.
   while (accept_count > 0)
   {
      wcout << receive(accept_evens) << L' ';
      --accept_count;
   }
}

이 예제의 결과는 다음과 같습니다.

0 2 4 6 8

필터 함수는 람다 함수, 함수 포인터 또는 함수 개체가 될 수 있습니다. 모든 필터 함수는 다음 형식 중 하나로 지정할 수 있습니다.

bool (_Type)
bool (_Type const &)

불필요하게 데이터를 복사하는 일이 없도록 하려면 값으로 전파되는 집계 형식인 경우 두 번째 형식을 사용합니다.

메시징 필터링은 구성 요소가 데이터를 받을 때 계산을 수행하는 데이터 흐름 프로그래밍 모델을 지원합니다. 메시지 전달 네트워크에서 데이터 흐름을 제어하는 데 필터 함수를 사용하는 예제를 보려면 방법: 메시지 블록 필터 사용, 연습: 사용자 지정 데이터 흐름 에이전트 만들기연습: 이미지 처리 네트워크 만들기를 참조하십시오.

[맨 위로 이동]

메시지 예약

메시지 예약을 사용하면 메시지 블록이 나중에 메시지를 사용하도록 예약할 수 있습니다. 일반적으로 메시지 예약이 직접 사용되지는 않습니다. 그러나 메시지 예약을 이해하면 미리 정의된 메시지 블록 형식의 일부 동작을 이해하는 데 도움이 됩니다.

greedy 조인 및 non-greedy 조인을 살펴봅니다. 둘 다 메시지 예약을 사용하여 나중에 메시지를 사용하도록 예약합니다. 앞에서 설명한 non-greedy 조인은 두 단계로 메시지를 받습니다. 첫 번째 단계에서는 non-greedy join 개체에서 각 소스가 메시지를 받을 때까지 기다립니다. 그런 다음 각 메시지를 예약하려고 시도합니다. non-greedy 조인이 각 메시지를 예약할 수 있는 경우 모든 메시지를 사용하고 대상에 전파합니다. 그렇지 않으면 메시지 예약을 해제하거나 취소하고 각 소스가 메시지를 받을 때까지 다시 기다립니다.

여러 소스에서 입력 메시지를 읽는 greedy 조인은 각 소스에서 메시지를 받기 위해 기다리는 동안 메시지 예약을 사용하여 추가 메시지를 읽습니다. 예를 들어 AB 메시지 블록에서 메시지를 받는 greedy 조인을 가정해 봅니다. greedy 조인이 B에서 두 개의 메시지를 받지만 A에서는 메시지를 받지 않은 경우 greedy 조인은 B의 두 번째 메시지에 대한 고유 메시지 식별자를 저장합니다. greedy 조인은 A에서 메시지를 받고 전파한 후 저장된 메시지 식별자를 사용하여 B의 두 번째 메시지를 계속해서 사용할 수 있는지 확인합니다.

고유 사용자 지정 메시지 블록 형식을 구현할 때 메시지 예약을 사용할 수 있습니다. 사용자 지정 메시지 블록 형식을 만드는 방법에 대한 예제를 보려면 연습: 사용자 지정 메시지 블록 만들기를 참조하십시오.

[맨 위로 이동]

참고 항목

개념

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

변경 기록

날짜

변경 내용

이유

2010년 8월

기본 예제를 추가했습니다.

향상된 기능 관련 정보