Asynchronous Agents Library – message block 4. ( call )
VC++ 10 Concurrency Runtime 2010. 7. 31. 17:00Asynchronous Agents Library
– message block 4. ( call )
작성자: 임준환( mumbi at daum dot net )
시작하는 글
이번 글에서는 call 이라는 message block 에 대해서 알아보겠습니다. 이번에 알아볼 call 또한 이미 알아본 message block 들과는 다른 특징이 있으니 특징을 이해하고, 필요할 때 바로 사용할 수 있어야 합니다.
call< _Type >
call 은 함수 포인터와 같은 역할을 합니다. call 에 message 를 보내면 그 message 는 생성자에서 지정한 함수 포인터의 인자로 사용됩니다. 그래서 call 에 message 를 보낸다는 것은 함수를 호출하는 것과 같은 효과를 얻을 수 있습니다.
특징
call 과 함수 호출의 다른 점이 있습니다. call 에 의해서 수행되는 함수를 동기 또는 비 동기로 수행되도록 선택할 수 있습니다.
동기 전달 함수인 send() 를 사용하여 message 를 전달하면 지정된 함수의 수행이 종료될 때까지 기다립니다. 그렇기 때문에 지정된 함수의 수행이 종료되어야 해당 context 가 진행됩니다.
반면에 비 동기 전달 함수인 asend() 를 사용하여 message 를 전달하면 지정된 함수의 수행이 종료될 때까지 기다리지 않고 호출한 context 는 계속 진행됩니다. 즉, 호출한 스레드와 call 의 함수가 동시에 진행됩니다.
이처럼 동기와 비 동기 처리를 적절히 선택하여 구현할 수 있습니다.
또 하나 기억해 두어야 할 특징은 하나의 call 객체에 message 를 여러 차례 보내더라도 동시에 수행되지 않습니다. 하나의 call 객체는 내부적으로 작업 큐( queue ) 를 가지고 있어 순차적으로 수행됩니다.
하지만 하나의 call 객체가 아니라 서로 다른 call 객체는 asend() 를 이용해 동시에 수행될 수 있습니다. 서로 각각의 작업 큐를 가지고 있기 때문입니다.
call 의 안타까운 점은 함수의 반환을 처리하지 못한다는 점입니다. 만약 지정된 함수의 결과를 얻어내야 한다면 해당 함수 내부에서 다른 message block 으로 결과를 message 로 보내야 합니다.
call 은 생성자 이외에 public 인 멤버 함수가 없습니다.
선언
사실 call 이라는 message block 의 타입은
template < class _Type, class _FunctorType = std::tr1::function< void( _Type const & ) > > class call
[ 코드1. call 의 선언 ]
입니다.
tr1 의 function 은 일반 함수 포인터와 멤버 함수 포인터, 함수 객체를 모두 가질 수 있으므로 기본 템플릿 매개변수인 _FunctorType 을 지정하지 않고 사용하는 것이 좋습니다.
위의 선언이 보여주듯이 _Type 은 수행될 함수의 매개변수의 타입입니다. bind 와 같은 어댑터 함수들과 함께 사용하여 여러 인자들을 바인딩할 수 있습니다.
예제
call 의 특징들을 쉽게 알아볼 수 있는 예제를 구현해보았습니다.
시나리오
Loader 라는 agent 클래스가 작업을 진행하고 call 을 이용해 진행 상황을 화면에 출력해주는 예제입니다.
코드
#include <iostream> #include <agents.h> using namespace std; using namespace Concurrency; class Loader : public agent { public: Loader( call< unsigned int >& displayDelegate ) : displayDelegate( displayDelegate ) { } protected: void run() { for( unsigned int percent = 0; percent <= 100; percent += 10 ) { Concurrency::wait( 1000 ); asend( this->displayDelegate, percent ); } this->done(); } private: call< unsigned int >& displayDelegate; }; int main() { call< unsigned int > showLoading( []( unsigned int percent ) { switch( percent ) { case 0: wcout << L"loading.."; break; case 100: wcout << endl << L"complete!" << endl; break; default: wcout << L".."; break; } } ); Loader loader( showLoading ); loader.start(); agent::wait( &loader ); }
[ 코드2. call 의 비 동기 처리를 이용한 예제 ]
Loader agent 의 작업 진행 상황을 화면에 출력합니다. 진행률이 0 일 때는 “loading.." 을 출력하고, 이후, 진행이 완료되기 전까지 “..” 을 추가하여 진행 상황을 알리고, 완료되면 “complete!” 를 출력하여, 작업의 완료를 알립니다.
[ 그림1. call 의 비 동기 처리를 이용한 예제 ]
마치는 글
이미 알아본 message block 들과는 많이 다른 call 에 대해서 알아보았습니다. call 은 함수의 수행을 동기 및 비 동기 처리로 할 수 있도록 도와주는 반면 결과를 얻기는 쉽지 않습니다.
이러한 call 의 단점의 보완과 더 많은 특징을 가지고 있는 transformer 라는 message block 을 제공합니다.
다음 글에서는 transformer 를 이용해 더욱 더 신나는 멀티 코어 프로그래밍을 해보도록 해보겠습니다.