Procedura: utilizzare i gruppi di pianificazione per influenzare l'ordine di esecuzione
Nel runtime di concorrenza l'ordine in cui vengono pianificate le attività non è deterministico.Tuttavia, è possibile utilizzare i criteri di pianificazione per influenzare l'ordine di esecuzione delle attività.In questo argomento viene illustrato come utilizzare i gruppi di pianificazione con il concurrency::SchedulingProtocol criteri di pianificazione per influenzare l'ordine in cui le operazioni eseguite.
Nell'esempio viene eseguito due volte un set di attività, ognuno con criteri di pianificazione diversi.Entrambi i tipi di criteri limitano il numero massimo di risorse di elaborazione a due.Nella prima esecuzione vengono utilizzati i criteri EnhanceScheduleGroupLocality , ovvero l'impostazione predefinita, mentre nella seconda vengono utilizzati i criteri EnhanceForwardProgress.Con i criteri EnhanceScheduleGroupLocality l'utilità di pianificazione esegue tutte le attività di un gruppo di pianificazione finché ogni attività non viene completata o restituita.Con i criteri EnhanceForwardProgress l'utilità di pianificazione si sposta nel gruppo di pianificazione successivo in modo round robin dopo il completamento o la restituzione di una sola l'attività.
Quando ogni gruppo di pianificazione contiene attività correlate, i criteri EnhanceScheduleGroupLocality determinano in genere un miglioramento delle prestazioni poiché la località della cache viene mantenuta tra le attività.I criteri EnhanceForwardProgress consentono alle attività di avanzare e sono utili quando è necessario pianificare equamente le risorse tra i gruppi di pianificazione.
Esempio
Questo esempio viene definito il work_yield_agent classe che deriva da concurrency::agent.La classe work_yield_agent esegue un'unità di lavoro, restituisce il contesto corrente, quindi esegue un'altra unità di lavoro.L'agente utilizza il concurrency::wait funzione di rendimento congiuntamente il contesto corrente in modo che possono essere eseguiti altri contesti.
In questo esempio vengono creati quattro oggetti work_yield_agent.Per illustrare come impostare i criteri dell'utilità di pianificazione per influenzare l'ordine di esecuzione degli agenti, i primi due agenti vengono associati a un gruppo di pianificazione mentre gli altri due agenti vengono associati a un altro gruppo di pianificazione.Nell'esempio viene utilizzata la concurrency::CurrentScheduler::CreateScheduleGroup metodo per creare il concurrency::ScheduleGroup oggetti.Tutti e quattro gli agenti vengono eseguiti due volte, ogni volta con criteri di pianificazione diversi.
// 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();
}
Questo esempio produce l'output che segue.
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...
Entrambi i tipi di criteri producono la stessa sequenza di eventi.Tuttavia, i criteri che utilizzano EnhanceScheduleGroupLocality avviano entrambi gli agenti che fanno parte del primo gruppo di pianificazione prima di avviare gli agenti che fanno parte del secondo gruppo.I criteri che utilizzano EnhanceForwardProgress avviano un agente del primo gruppo, quindi avviano il primo agente del secondo gruppo.
Compilazione del codice
Copiare il codice di esempio e incollarlo in un progetto di Visual Studio o incollarlo in un file denominato protocol.cpp pianificazione e quindi eseguire il comando riportato di seguito in una finestra del prompt dei comandi di Visual Studio.
cl.exe /EHsc scheduling-protocol.cpp