Concurrency Runtime – Task Scheduler 3. ( ScheduleGroup )
VC++ 10 Concurrency Runtime 2010. 9. 27. 08:30Concurrency Runtime
– Task Scheduler 3. ( ScheduleGroup )
작성자: 임준환( mumbi at daum dot net )
시작하는 글
Scheduler 는 항상 하나 이상의 스케줄 그룹을 가지고 있습니다. 스케줄 그룹이 무엇인지 알아보고 동시성 프로그래밍에 어떤 영향을 주는지 알아보도록 하겠습니다.
SchedulerGroup
SchedulerGroup 이란 Scheduler 가 스케줄링 해야 할 작업들을 모아 놓은 스케줄 그룹을 대표하는 클래스입니다.
정책
이전 글에서 본 스케줄러 정책들 중 SchedulingProtocol 정책은 스케줄링 순서를 변경하여 작업들의 처리 순서를 변경할 수 있습니다. ScheduleringProtocol 정책이 EnhanseScheduleGroupLocality 로 설정된 경우에는 스케줄링 중인 스케줄 그룹을 변경하지 않고, 현재 스케줄링 중인 스케줄 그룹을 다른 스케줄 그룹보다 먼저 처리합니다. 반면에 SchedulingProtocol 정책이 EnhanseForwardProgress 로 설정된 경우에는 다른 스케줄링 그룹의 작업을 처리합니다. 스케줄 그룹 간에 작업을 공평하게 처리하게 됩니다.
생성
CurrentScheduler::CreateScheduleGroup() 또는 Scheduler::CreateScheduleGroup() 을 통해 ScheduleGroup 객체를 생성할 수 있습니다.
Scheduler 가 참조 개수를 사용하여 수명을 관리하는 것처럼 ScheduleGroup 객체 또한 참조 개수를 사용합니다. 생성되는 순간 참조 개수는 1이 됩니다. SchedulerGroup::Reference() 는 참조 개수는 1이 증가하고, SchedulerGroup::Release() 는 참조 개수가 1이 감소하게 됩니다.
동작 및 사용
Scheduler 객체에는 기본적으로 기본 ScheduleGroup 을 가지고 있습니다. 명시적으로 ScheduleGroup 을 생성하지 않는다면 스케줄링 해야 할 작업들은 기본 ScheduleGroup 에 추가됩니다.
ScheduleGroup 은 Concurrency Runtime 중 Asynchronous Agents Library( 이하, AAL ) 에서 사용할 수 있습니다. agent 클래스나 message block 들의 생성자로 ScheduleGroup 을 전달하여 사용할 수 있습니다.
예제
ScheduleGroup 을 어떻게 사용하는지 이해를 도울 예제를 살펴보겠습니다.
시나리오
단순하게 어떤 스케줄 그룹의 작업들이 어떤 순서로 처리되는지 알아보는 예제입니다.
코드
#include <agents.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
using namespace std;
using namespace Concurrency;
#pragma optimize( "", off )
void spin_loop()
{
for( unsigned int i = 0; i < 500000000; ++i )
{
}
}
#pragma optimize( "", on )
class work_yield_agent
: public agent
{
public:
explicit work_yield_agent( unsigned int group_number, unsigned int task_number )
: group_number( group_number )
, task_number( task_number ) { }
explicit work_yield_agent( ScheduleGroup& scheduleGroup, unsigned int group_number, unsigned int task_number )
: agent( scheduleGroup )
, group_number( group_number )
, task_number( task_number ) { }
protected:
void run()
{
wstringstream header, ss;
header << L"agent no. " << this->group_number << L"-" << this->task_number << L": ";
ss << header.str() << L"first loop..." << endl;
wcout << ss.str();
spin_loop();
ss = wstringstream();
ss << header.str() << L"waiting..." << endl;
wcout << ss.str();
Concurrency::wait( 0 );
ss = wstringstream();
ss<< header.str() << L"second loop..." << endl;
wcout << ss.str();
spin_loop();
ss = wstringstream();
ss << header.str() << L"finished..." << endl;
wcout << ss.str();
this->done();
}
private:
unsigned int group_number;
unsigned int task_number;
};
void run_agents()
{
const unsigned int group_count = 2;
const unsigned int tasks_per_group = 2;
vector< ScheduleGroup* > groups;
vector< agent* > agents;
for( unsigned int group_index = 0; group_index < group_count; ++group_index )
{
groups.push_back( CurrentScheduler::CreateScheduleGroup() );
for( unsigned int task_index = 0; task_index < tasks_per_group; ++task_index )
{
agents.push_back( new work_yield_agent( *groups.back(), group_index, task_index ) );
}
}
for_each( agents.begin(), agents.end(), []( agent* pAgent )
{
pAgent->start();
} );
agent::wait_for_all( agents.size(), &agents[0] );
for_each( agents.begin(), agents.end(), []( agent* pAgent )
{
delete pAgent;
} );
for_each( groups.begin(), groups.end(), []( ScheduleGroup* pScheduleGroup )
{
pScheduleGroup->Release();
} );
}
int main()
{
wcout << L"Using EnhanceScheduleGroupLocality..." << endl;
CurrentScheduler::Create( SchedulerPolicy( 3,
MinConcurrency, 1,
MaxConcurrency, 2,
SchedulingProtocol, EnhanceScheduleGroupLocality ) );
run_agents();
CurrentScheduler::Detach();
wcout << endl << endl;
wcout << L"Using EnhanceForwardProgress..." << endl;
CurrentScheduler::Create( SchedulerPolicy( 3,
MinConcurrency, 1,
MaxConcurrency, 2,
SchedulingProtocol, EnhanceForwardProgress ) );
run_agents();
CurrentScheduler::Detach();
}
[ 코드1. 처리되는 스케줄 그룹의 순서를 확인하는 예제 ]
최대 동시성 리소스( computing core ) 의 개수를 2개로 하고, 한 번은 SchedulingProtocol 정책을 EnhanceScheduleGroupLocality 로 하고, 한 번은 EnhanceForwardProgress 으로 하여 작업의 순서를 알아보았습니다.
2개의 스케줄 그룹을 생성하고, 하나의 그룹에 2개의 작업을 추가하여, 총 4개의 작업이 수행됩니다.
예제의 spin_loop() 는 어떤 작업을 처리하는 것을 의미하고 wait() 는 양보를 뜻합니다. wait() 가 호출되면 다른 작업이 스케줄링 되는데 이 때 결정되는 작업이 어떤 작업인지는 앞서 설정한 SchedulingProtocol 정책에 따릅니다.
EnhanceScheduleGroupLocality 로 설정된 경우에는 같은 스케줄 그룹 내의 작업이 처리되는 반면에, EnhanceForwardProgress 로 설정된 경우에는 다른 스케줄 그룹의 작업이 처리되게 됩니다.
[ 그림1. 처리되는 스케줄 그룹의 순서를 확인하는 예제 실행 결과 ]
마치는 글
이렇게 스케줄링 정책과 스케줄 그룹을 통해서 스케줄링 순서를 결정하는데 관여할 수 있다는 것을 알아보았습니다.
다음 글에서는 스레드를 직접 생성하여 사용할 수 있는 ScheduleTask 라는 것에 대해서 알아보도록 하겠습니다.
'VC++ 10 Concurrency Runtime' 카테고리의 다른 글
| Concurrency Runtime – Task Scheduler 5. ( Context blocking ) (1) | 2010.10.12 |
|---|---|
| Concurrency Runtime – Task Scheduler 4. ( ScheduleTask ) (0) | 2010.10.04 |
| Concurrency Runtime – Task Scheduler 2. ( SchedulerPolicy ) (0) | 2010.09.18 |
| Concurrency Runtime – Task Scheduler 1. ( Scheduler ) (0) | 2010.09.02 |
| Concurrency Runtime - 만델브로트 프랙탈 ( Mandelbrot Fractal ) 예제 (3) | 2010.08.28 |

