Asynchronous 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 의 비 동기 처리를 이용한 예제 ]

[ 그림1. call 의 비 동기 처리를 이용한 예제 ]

 

마치는 글

 이미 알아본 message block 들과는 많이 다른 call 에 대해서 알아보았습니다. call 은 함수의 수행을 동기 및 비 동기 처리로 할 수 있도록 도와주는 반면 결과를 얻기는 쉽지 않습니다.

 이러한 call 의 단점의 보완과 더 많은 특징을 가지고 있는 transformer 라는 message block 을 제공합니다.

 다음 글에서는 transformer 를 이용해 더욱 더 신나는 멀티 코어 프로그래밍을 해보도록 해보겠습니다.

신고