방법: 실행 순서에 영향을 주는 일정 그룹 사용
동시성 런타임에서 작업의 예약 순서는 명확하지 않습니다.그러나 일정 정책을 사용하면 작업이 실행되는 순서에 영향을 줄 수 있습니다.이 항목 예약 그룹을 함께 사용 하는 방법을 보여 줍니다.의 concurrency::SchedulingProtocol 는 작업 실행 순서를 정하는 데 스케줄러 정책.
이 예제에서는 각각 다른 일정 정책을 사용하여 작업 집합을 두 번 실행합니다.이때 두 정책 모두 최대 처리 리소스의 수를 2개로 제한합니다.첫 번째 실행에서는 EnhanceScheduleGroupLocality 정책(기본값)을 사용하고 두 번째 실행에서는 EnhanceForwardProgress 정책을 사용합니다.EnhanceScheduleGroupLocality 정책을 사용하는 경우 스케줄러는 각 작업이 끝나거나 양보할 때까지 한 일정 그룹의 모든 작업을 실행합니다.반면에 EnhanceForwardProgress 정책을 사용하는 경우 스케줄러는 한 작업이 끝나거나 양보하는 즉시 라운드 로빈 방식에 따라 다음 일정 그룹으로 이동합니다.
각 일정 그룹에 관련 작업이 포함된 경우에는 작업 간에 캐시 집약성이 유지되므로 일반적으로 EnhanceScheduleGroupLocality 정책을 사용하면 성능이 향상됩니다.EnhanceForwardProgress 정책을 사용하면 작업을 순서에 따라 앞으로 진행할 수 있으므로 일정 그룹 간 예약이 공평해야 할 경우에 유용합니다.
예제
이 예제에서는 정의 work_yield_agent 에서 파생 되는 클래스, concurrency::agent.work_yield_agent 클래스는 한 작업 단위를 수행하고 현재 컨텍스트를 양보한 다음, 다른 작업 단위를 수행합니다.에이전트를 사용 하 여 concurrency::wait 방식의 다른 컨텍스트에서 실행 될 수 있도록 현재 컨텍스트를 생성 하는 함수입니다.
이 예제에서는 네 개의 work_yield_agent 개체를 만듭니다.스케줄러 정책을 설정하여 에이전트 실행 순서에 영향을 주는 방법을 나타내기 위해 이 예제에서는 먼저 두 에이전트를 한 일정 그룹과 연결하고 다른 두 에이전트를 다른 일정 그룹과 연결합니다.이 예제를 사용 하는 concurrency::CurrentScheduler::CreateScheduleGroup 메서드를 만들 수는 concurrency::ScheduleGroup 개체.또한 매번 다른 일정 정책을 사용하여 네 개의 에이전트 모두 두 번 실행합니다.
// scheduling-protocol.cpp
// compile with: /EHsc
#include <agents.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
#pragma optimize( "", off )
// Simulates work by performing a long spin loop.
void spin_loop()
{
for (int i = 0; i < 500000000; ++i)
{
}
}
#pragma optimize( "", on )
// Agent that performs some work and then yields the current context.
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(Scheduler& scheduler,
unsigned int group_number, unsigned int task_number)
: agent(scheduler)
, _group_number(group_number)
, _task_number(task_number)
{
}
explicit work_yield_agent(ScheduleGroup& group,
unsigned int group_number, unsigned int task_number)
: agent(group)
, _group_number(group_number)
, _task_number(task_number)
{
}
protected:
// Performs the work of the agent.
void run()
{
wstringstream header, ss;
// Create a string that is prepended to each message.
header << L"group " << _group_number
<< L",task " << _task_number << L": ";
// Perform work.
ss << header.str() << L"first loop..." << endl;
wcout << ss.str();
spin_loop();
// Cooperatively yield the current context.
// The task scheduler will then run all blocked contexts.
ss = wstringstream();
ss << header.str() << L"waiting..." << endl;
wcout << ss.str();
concurrency::wait(0);
// Perform more work.
ss = wstringstream();
ss << header.str() << L"second loop..." << endl;
wcout << ss.str();
spin_loop();
// Print a final message and then set the agent to the
// finished state.
ss = wstringstream();
ss << header.str() << L"finished..." << endl;
wcout << ss.str();
done();
}
private:
// The group number that the agent belongs to.
unsigned int _group_number;
// A task number that is associated with the agent.
unsigned int _task_number;
};
// Creates and runs several groups of agents. Each group of agents is associated
// with a different schedule group.
void run_agents()
{
// The number of schedule groups to create.
const unsigned int group_count = 2;
// The number of agent to create per schedule group.
const unsigned int tasks_per_group = 2;
// A collection of schedule groups.
vector<ScheduleGroup*> groups;
// A collection of agents.
vector<agent*> agents;
// Create a series of schedule groups.
for (unsigned int group = 0; group < group_count; ++group)
{
groups.push_back(CurrentScheduler::CreateScheduleGroup());
// For each schedule group, create a series of agents.
for (unsigned int task = 0; task < tasks_per_group; ++task)
{
// Add an agent to the collection. Pass the current schedule
// group to the work_yield_agent constructor to schedule the agent
// in this group.
agents.push_back(new work_yield_agent(*groups.back(), group, task));
}
}
// Start each agent.
for_each(begin(agents), end(agents), [](agent* a) {
a->start();
});
// Wait for all agents to finsih.
agent::wait_for_all(agents.size(), &agents[0]);
// Free the memory that was allocated for each agent.
for_each(begin(agents), end(agents), [](agent* a) {
delete a;
});
// Release each schedule group.
for_each(begin(groups), end(groups), [](ScheduleGroup* group) {
group->Release();
});
}
int wmain()
{
// Run the agents two times. Each run uses a scheduler
// policy that limits the maximum number of processing resources to two.
// The first run uses the EnhanceScheduleGroupLocality
// scheduling protocol.
wcout << L"Using EnhanceScheduleGroupLocality..." << endl;
CurrentScheduler::Create(SchedulerPolicy(3,
MinConcurrency, 1,
MaxConcurrency, 2,
SchedulingProtocol, EnhanceScheduleGroupLocality));
run_agents();
CurrentScheduler::Detach();
wcout << endl << endl;
// The second run uses the EnhanceForwardProgress
// scheduling protocol.
wcout << L"Using EnhanceForwardProgress..." << endl;
CurrentScheduler::Create(SchedulerPolicy(3,
MinConcurrency, 1,
MaxConcurrency, 2,
SchedulingProtocol, EnhanceForwardProgress));
run_agents();
CurrentScheduler::Detach();
}
이 예제의 결과는 다음과 같습니다.
Using EnhanceScheduleGroupLocality...
group 0,task 0: first loop...
group 0,task 1: first loop...
group 0,task 0: waiting...
group 1,task 0: first loop...
group 0,task 1: waiting...
group 1,task 1: first loop...
group 1,task 0: waiting...
group 0,task 0: second loop...
group 1,task 1: waiting...
group 0,task 1: second loop...
group 0,task 0: finished...
group 1,task 0: second loop...
group 0,task 1: finished...
group 1,task 1: second loop...
group 1,task 0: finished...
group 1,task 1: finished...
Using EnhanceForwardProgress...
group 0,task 0: first loop...
group 1,task 0: first loop...
group 0,task 0: waiting...
group 0,task 1: first loop...
group 1,task 0: waiting...
group 1,task 1: first loop...
group 0,task 1: waiting...
group 0,task 0: second loop...
group 1,task 1: waiting...
group 1,task 0: second loop...
group 0,task 0: finished...
group 0,task 1: second loop...
group 1,task 0: finished...
group 1,task 1: second loop...
group 0,task 1: finished...
group 1,task 1: finished...
두 정책 모두 결과 이벤트 시퀀스가 같습니다.그러나 EnhanceScheduleGroupLocality를 사용하는 정책은 두 번째 그룹에 포함된 에이전트를 시작하기 전에 첫 번째 일정 그룹에 포함된 두 에이전트를 모두 시작합니다.반면에 EnhanceForwardProgress를 사용하는 정책은 첫 번째 그룹의 한 에이전트를 시작한 다음, 두 번째 그룹의 첫 번째 에이전트를 시작합니다.
코드 컴파일
예제 코드를 복사 하 고 Visual Studio 프로젝트에 붙여 또는 라는 파일에 붙여 예약 protocol.cpp 및 다음 Visual Studio 명령 프롬프트 창에서 다음 명령을 실행 합니다.
cl.exe /EHsc scheduling-protocol.cpp