Como: Use grupos de cronograma para influenciar a ordem de execução
Em tempo de execução de concorrência, a ordem em que as tarefas são agendadas é não determinística.No entanto, você pode usar políticas de programação para influenciar a ordem em que executar tarefas.Este tópico mostra como usar grupos de cronograma juntamente com a política de agendador de concurrency::SchedulingProtocol para influenciar a ordem em que executar tarefas.
O exemplo reproduz um conjunto de tarefas duas vezes, cada um com uma diretiva diferente de programação.Ambas as diretivas limitam o número máximo de recursos de processamento para dois.O executa usa a diretiva de EnhanceScheduleGroupLocality, que é o padrão, e a segunda execução usa a diretiva de EnhanceForwardProgress .Na diretiva de EnhanceScheduleGroupLocality , o agendador executa todas as tarefas em um grupo de cronograma até que cada tarefa termina ou gere.Na diretiva de EnhanceForwardProgress , os movimentos de agendador para o próximo grupo de cronograma de uma maneira round robin depois que apenas uma tarefa termina ou produz.
Quando cada grupo de cronograma contém tarefas relacionadas, a política de EnhanceScheduleGroupLocality normalmente resulta em melhor desempenho porque a localidade do cache é preservada entre tarefas.A política de EnhanceForwardProgress tarefas permite fazer o progresso para frente e é útil quando você precisar de equidade de programação através dos grupos de cronograma.
Exemplo
Este exemplo define a classe de work_yield_agent , que deriva de concurrency::agent.A classe de work_yield_agent executa uma unidade de trabalho, produz o contexto atual e, em outra unidade de trabalho.O agente de concurrency::wait usa a função para produzir cooperativa o contexto atual para que outros contextos podem executar.
Este exemplo cria quatro objetos de work_yield_agent .Para ilustrar como definir políticas de agendador para afetar a ordem em que a execução de agentes, o exemplo associa os primeiros dois agentes com o grupo de um cronograma e os outros dois agentes com outro grupo de cronograma.O exemplo usa o método de concurrency::CurrentScheduler::CreateScheduleGroup para criar os objetos de concurrency::ScheduleGroup .O exemplo reproduz todos os quatro agentes duas vezes em cada vez, com uma diretiva diferente de programação.
// 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();
}
O exemplo produz a seguinte saída.
Ambas as diretivas geram a mesma sequência de eventos.No entanto, a diretiva que usa começa de EnhanceScheduleGroupLocality ambos os agentes que fazem parte do primeiro grupo de cronograma antes de iniciar agentes que fazem parte do segundo grupo.A diretiva que usa começa de EnhanceForwardProgress um agente do primeiro grupo e então inicia o primeiro agente no segundo grupo.
Compilando o código
Copie o código de exemplo e cole-o em um projeto do Visual Studio, ou cole em um arquivo denominado scheduling-protocol.cpp e execute o seguinte comando em uma janela de prompt de comando do Visual Studio.
cl.exe /EHsc scheduling-protocol.cpp