Asynchronous Agents Library – message block 6. ( choice )
VC++ 10 Concurrency Runtime 2010. 8. 6. 08:30Asynchronous 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_assignment 의 has_value() 를 호출합니다.
size_t index()
가지고 있는 message block 의 index 를 반환합니다. 내부적으로 single_assignment 의 value() 를 호출하고 하고, 그 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 판별 예제 ]
마치는 글
이번에는 tuple 을 이용하는 choice 에 대해서 알아보았습니다. Visual studio 2010 에서 지원하는 C++0x 문법을 사용하면 좀 더 쉽게 사용할 수 있습니다.
다음 글 또한 새로운 message block 에 대해서 알아보도록 하겠습니다.