Cara: Menggunakan Grup Jadwal untuk Memengaruhi Urutan Eksekusi

Dalam Runtime Konkurensi, urutan tugas dijadwalkan tidak deterministik. Namun, Anda dapat menggunakan kebijakan penjadwalan untuk memengaruhi urutan tugas yang dijalankan. Topik ini menunjukkan cara menggunakan grup jadwal bersama dengan kebijakan penjadwal konkurensi::SchedulingProtocol untuk memengaruhi urutan tugas yang dijalankan.

Contoh menjalankan serangkaian tugas dua kali, masing-masing dengan kebijakan penjadwalan yang berbeda. Kedua kebijakan membatasi jumlah maksimum sumber daya pemrosesan menjadi dua. Eksekusi pertama menggunakan EnhanceScheduleGroupLocality kebijakan, yang merupakan default, dan eksekusi kedua menggunakan EnhanceForwardProgress kebijakan. EnhanceScheduleGroupLocality Di bawah kebijakan, penjadwal menjalankan semua tugas dalam satu grup jadwal hingga setiap tugas selesai atau menghasilkan. EnhanceForwardProgress Di bawah kebijakan, penjadwal berpindah ke grup jadwal berikutnya dengan cara round-robin setelah hanya satu tugas selesai atau menghasilkan.

Ketika setiap grup jadwal berisi tugas terkait, EnhanceScheduleGroupLocality kebijakan biasanya menghasilkan peningkatan performa karena lokalitas cache dipertahankan di antara tugas. Kebijakan ini EnhanceForwardProgress memungkinkan tugas untuk meneruskan kemajuan dan berguna ketika Anda memerlukan penjadwalan kewajaran di seluruh grup jadwal.

Contoh

Contoh ini mendefinisikan work_yield_agent kelas, yang berasal dari konkurensi::agent. Kelas work_yield_agent melakukan satu unit pekerjaan, menghasilkan konteks saat ini, dan kemudian melakukan unit kerja lain. Agen menggunakan fungsi konkurensi::tunggu untuk secara kooperatif menghasilkan konteks saat ini sehingga konteks lain dapat berjalan.

Contoh ini membuat empat work_yield_agent objek. Untuk mengilustrasikan cara mengatur kebijakan penjadwal untuk memengaruhi urutan agen berjalan, contohnya mengaitkan dua agen pertama dengan satu grup jadwal dan dua agen lainnya dengan grup jadwal lain. Contoh menggunakan metode konkurensi::CurrentScheduler::CreateScheduleGroup untuk membuat objek konkurensi::ScheduleGroup . Contoh menjalankan keempat agen dua kali, setiap kali dengan kebijakan penjadwalan yang berbeda.

// 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();
}

Contoh ini menghasilkan output berikut.

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...

Kedua kebijakan menghasilkan urutan peristiwa yang sama. Namun, kebijakan yang menggunakan EnhanceScheduleGroupLocality memulai kedua agen yang merupakan bagian dari grup jadwal pertama sebelum memulai agen yang merupakan bagian dari grup kedua. Kebijakan yang menggunakan EnhanceForwardProgress memulai satu agen dari grup pertama lalu memulai agen pertama di grup kedua.

Mengompilasi Kode

Salin kode contoh dan tempelkan dalam proyek Visual Studio, atau tempelkan dalam file yang diberi nama scheduling-protocol.cpp lalu jalankan perintah berikut di jendela Prompt Perintah Visual Studio.

cl.exe /EHsc scheduling-protocol.cpp

Baca juga

Jadwalkan Grup
Agen Asinkron