Asynchronous Agents Library
– message block 6. ( choice )

작성자: 임준환( mumbi at daum dot net )

 

시작하는 글

 unbounded_buffer, overwrite_buffer 그리고  single_assignment 와 같이 message 를 보관하는 버퍼 기능의 message block 들과, call, transformer 와 같이 지정된 함수형 객체를 수행하는 기능의 message block 들에 대해서 알아보았습니다.

 이번 글에서는 message block 들의 상태를 기반으로 동작하는 message block 들 중 choice 에 대해서 알아보도록 하겠습니다.

 

choice< _TupleType >

 choice 의 특징은 지정된 message block 들 중 가장 먼저 messge 를 가져올 수 있는 message block 을 가리키는 기능입니다. 다시 설명하면 어떤 message 를 받을 준비가 된 message block 들을 choice 에 지정한 후, 지정된 message block 들 중 가장 먼저 message 를 받은 message block 을 choice 가 가리키게 됩니다.

 

tuple 의 조건

 choice 은 message 를 받을 준비가 된 message block 들을 지정할 때 tr1 의 tuple 을 사용합니다. 이 글의 주제가 tr1 이 아닌 만큼 tuple 에 대한 자세한 설명은 생략하겠습니다. 간략히 설명하면 std::pair 의 일반화된 객체라고 생각하시면 됩니다. std::pair 가 2개의 타입을 저장할 수 있다면 tuple 은 10개의 여러 가지 타입을 저장할 수 있습니다.

 그래서 다른 message block 과 달리 템플릿 매개변수가 message 타입이 아닌 여러 message block 들의 타입을 저장할 수 있는 tuple 의 타입입니다.

 하지만 아무 tuple 이나 사용할 수 있는 것이 아닙니다. 약간의 조건을 충족시키는 tuple 이어야만 합니다.

  • tuple 의 구성 요소로 source_type 이라는 typedef 를 가지고 있는 message block 들이어야 한다. ( message block 중 call 은 source_type 이 정의되어 있지 않기 때문에 사용할 수 없습니다. )
  • message block 들은 복사 생성자와 배정 연산자( = ) 의 사용을 금지( private )하고 있으므로 tuple 의 구성 요소로 포인터 타입을 사용해야 한다.

 

특징

 choice 는 여러 message block 들 중 가장 먼저 message 를 받은 message block 을 가리킵니다. 그 말은 choice 는 tuple 로 지정된 message block 들의 index 를 가지고 있다는 말입니다.

 그런데 그 index 를 저장하기 위한 변수로 내부적으로 single_assignment 를 사용합니다. 그 뜻은 single_assignment 는 단 한 번만 값을 저장할 수 있기 때문에 choice 는 가리키게 된 message block 의 index 를 변경할 수 없습니다. 즉, 일회성이라는 말이 됩니다.

 receive() 로 choice 의 message 를 받아오면 지정된 message block 들 중 하나라도 message 를 받을 때까지 기다리다가 message 를 받은 message block 이 생기면 그 message block 의 index 를 반환합니다.

 위의 특징을 응용하면 여러 message block 들 중 하나라도 message 를 받을 때까지 대기하는 기능으로 사용할 수 있습니다.

 

멤버 함수

 생성자와 소멸자를 제외한 public 인 멤버 함수 중 인터페이스로 재정의된 함수들을 제외한 함수들입니다.

인터페이스를 재정의한 멤버 함수들은 이전 글인 2010/07/10 - [Language Development/VC++ 10 Concurrency Runtime] - Asynchronous Agents Library – message block 1. ( 인터페이스 ) 를 참고하시기 바랍니다. 

 

bool has_value() const

 현재 message block 의 index 를 가지고 있는지 반환합니다. 내부적으로 single_assignmenthas_value() 를 호출합니다.

 

size_t index()

 가지고 있는 message block 의 index 를 반환합니다. 내부적으로 single_assignmentvalue() 를 호출하고 하고, 그 value() 는 receive() 를 사용하기 때문에 아직 index 를 가지고 있지 않다면 가질 때까지 대기하게 됩니다.

 

template <typename _Payload_type>
_Payload_type const & value()

 choice 가 가리키고 있는 index 의 message block 의 message 의 값을 반환합니다. message 의 타입을 명시적으로 호출해야 하기 때문에 사용하기 불편합니다.

 

헬퍼 함수

 choice 는 tuple 을 사용하기 때문에 선언이 복잡할 수 밖에 없습니다. 그래서 조금 더 쉽게 선언을 할 수 있도록 헬퍼 함수를 제공합니다.

unbounded_buffer< int > i;
unbounded_buffer< float > f;
unbounded_buffer< bool > b;

tuple< unbounded_buffer< int >*, unbounded_buffer< float >*, unbounded_buffer< bool >* > tp( &i, &f, &b );
choice< tuple< unbounded_buffer< int >*, unbounded_buffer< float >*, unbounded_buffer< bool >* > > ch( tp );

[ 코드1. 헬퍼 함수를 사용하지 않은 choice 선언 ]

 헬퍼 함수를 사용하지 않으면 템플릿 매개변수인 타입들을 모두 적어야 하기 때문에 굉장히 길어집니다. 특히나 message block 들의 타입이 굉장히 길기 때문에 더욱 불편합니다. typedef 를 사용하더라도 길기는 마찬가지 입니다.

 

unbounded_buffer< int > i;
unbounded_buffer< float > f;
unbounded_buffer< bool > b;

auto ch = make_choice( &i, &f, &b );

[ 코드2. 헬퍼 함수를 사용한 choice 선언 ]

 헬퍼 함수와 Visual studio 2010 에서 지원하는 C++0x 문법인 auto 를 사용하여 더욱 더 짧게 선언할 수 있습니다.

 

예제

 choice 의 특징을 쉽게 알아볼 수 있는 예제를 구현해보겠습니다.

 

시나리오

 두 agent 를 사용해서 어떤 agent 가 더 빨리 수행되는가를 육상에 비유하여 표현해보았습니다. choice 객체는 두 agent 중 빨리 수행한 쪽을 판별하는 역할을 합니다.

 

코드

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

using namespace std;
using namespace Concurrency;

class Runner
	: public agent
{
public:
	Runner( ITarget< bool >& target, const wstring& name )
		: target( target )
		, name( name ) { }

	const wstring& GetName() const
	{
		return this->name;
	}

protected:
	void run()
	{
		for( unsigned int i = 0; i < 1000; ++i )
		{
			if( 0 == i % 100 )
				wcout << this->name << L" - " << i << L"m." << endl;
		}

		wcout << this->name << L" - " << L"Finished!" << endl;

		asend( target, true );

		this->done();
	}

private:
	ITarget< bool >&	target;
	wstring				name;
};

int main()
{
	single_assignment< bool > isFinished[2];

	auto winnerRecoder = make_choice( &isFinished[0], &isFinished[1] );

	Runner runner[2] = { Runner( isFinished[0], L"Carl Lewis" ), Runner( isFinished[1], L"Usain Bolt" ) };
	runner[0].start();
	runner[1].start();

	agent* agents[2] = { &runner[0], &runner[1] };
	agent::wait_for_all( 2, agents );

	wcout << runner[ receive( winnerRecoder ) ].GetName() << L" is winner!" << endl;
}

[ 코드3. choice 를 사용한 먼저 수행된 agent 판별 예제 ]

 두 agent 에 육상 선수인 칼루이스와 우사인 볼트의 이름을 붙여보았습니다. 각 agent 는 수행이 완료되면 완료 message 를 single_assignment 에 보내고, choice 객체는 완료 message 가 먼저 도착한 message block 의 index 를 갖게 됩니다.

 그 index 를 이용해 해당 agent 의 육상 선수 이름을 출력합니다.

 이 예제의 결과는 스케쥴링에 따라 다르기 때문에 실행할 때마다 결과가 다를 수 있습니다.

 

[ 그림1. choice 를 사용한 먼저 수행된 agent 판별 예제 ]

[ 그림1. choice 를 사용한 먼저 수행된 agent 판별 예제 ]

 

마치는 글

 이번에는 tuple 을 이용하는 choice 에 대해서 알아보았습니다. Visual studio 2010 에서 지원하는 C++0x 문법을 사용하면 좀 더 쉽게 사용할 수 있습니다.

다음 글 또한 새로운 message block 에 대해서 알아보도록 하겠습니다.