共用方式為


如何:使用排程群組來影響執行順序

在並行執行階段中,工作的排定順序並不是絕對的。 不過,您可以使用排程原則來影響執行工作的順序。 本主題顯示如何搭配使用排程群組與 concurrency::SchedulingProtocol 排程器原則,以影響工作的執行順序。

這個範例會執行同一組工作兩次,每次都使用不同的排程原則。 這兩個原則都會將處理資源數目上限限制為 2。 第一次執行會使用 EnhanceScheduleGroupLocality 原則 (預設值),而第二次執行則會使用 EnhanceForwardProgress 原則。 在 EnhanceScheduleGroupLocality 原則下,排程器會執行一個排程群組中的所有工作,直到完成或產生每個工作為止。 在 EnhanceForwardProgress 原則下,排程器會在完成或產生一個工作之後,立即使用循環配置資源方式移至下一個排程群組。

EnhanceScheduleGroupLocality 原則用在排程群組時,因為每個排程群組都包含相關聯的工作,而這個原則會保留工作之間的快取位置,因此效能會得到改善。 EnhanceForwardProgress 原則則可讓工作往前進行,當您需要公平排定所有排程群組時,這個原則會很有用。

範例

這個範例定義衍生自 concurrency::agentwork_yield_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();
}

這個範例產生下列輸出。

  

這兩個原則都會產生相同的事件序列。 不過,使用 EnhanceScheduleGroupLocality 的原則會先啟動屬於第一個排程群組的兩個代理程式,再啟動屬於第二個群組的代理程式。 使用 EnhanceForwardProgress 的原則會啟動第一個群組中的某個代理程式,然後再啟動第二個群組中的第一個代理程式。

編譯程式碼

請複製範例程式碼,並將它貼在 Visual Studio 專案中,或貼在名為 scheduling-protocol.cpp 的檔案中,然後在 Visual Studio 命令提示字元視窗中執行下列命令。

cl.exe /EHsc scheduling-protocol.cpp

請參閱

概念

排程群組

非同步代理程式