如何:使用排程群組來影響執行順序
在並行運行時間中,排程工作的順序不具決定性。 不過,您可以使用排程原則來影響工作執行的順序。 本主題說明如何使用排程群組與 並行::ScheduleProtocol 排程器原則,以影響工作執行的順序。
此範例會執行一組工作兩次,每個工作都有不同的排程原則。 這兩個原則會將處理資源數目上限限制為兩個。 第一次執行會 EnhanceScheduleGroupLocality
使用預設的原則,而第二次執行會使用原則 EnhanceForwardProgress
。 在原則下 EnhanceScheduleGroupLocality
,排程器會在一個排程群組中執行所有工作,直到每個工作完成或產生為止。 根據原則 EnhanceForwardProgress
,排程器只會在一個工作完成或產生之後,以迴圈配置資源方式移至下一個排程群組。
當每個排程群組包含相關工作時,原則通常會導致效能改善, EnhanceScheduleGroupLocality
因為會在工作之間保留快取區域。 此原則 EnhanceForwardProgress
可讓工作向前推進,而且當您需要跨排程群組排程公平性時很有用。
範例
此範例會 work_yield_agent
定義 衍生自 concurrency::agent 的 類別。 類別 work_yield_agent
會執行工作單位、產生目前的內容,然後執行另一個工作單位。 代理程式會使用 並行::wait 函式來合作產生目前的內容,讓其他內容可以執行。
此範例會建立四個 work_yield_agent
物件。 為了說明如何設定排程器原則來影響代理程式執行的順序,此範例會將前兩個代理程式與一個排程群組建立關聯,另一個代理程式與另一個排程群組產生關聯。 此範例會使用 concurrency::CurrentScheduler::CreateScheduleGroup 方法來建立 並行::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 專案中,或貼到名為 scheduling-protocol.cpp
的檔案中,然後在 Visual Studio 命令提示字元視窗中執行下列命令。
cl.exe /EHsc scheduling-protocol.cpp