Partager via


Comment : créer des agents qui utilisent des stratégies de planificateur spécifiques

Un agent est un composant d'application qui fonctionne de façon asynchrone avec d'autres composants en vue d'effectuer de plus grandes tâches de calcul. Un agent a en général un cycle de vie fixe et conserve l'état.

Chaque agent peut avoir des spécifications d'application uniques. Par exemple, un agent qui active l'intervention de l'utilisateur (extraction de l'entrée ou affichage de la sortie) peut nécessiter un accès à priorité plus élevée aux ressources de calcul. Les stratégies du planificateur vous permettent de contrôler la stratégie utilisée par le planificateur lorsqu'il gère des tâches. Cette rubrique explique comment créer des agents qui utilisent des stratégies de planificateur spécifiques.

Pour obtenir un exemple de base qui utilise des stratégies du planificateur personnalisées avec des blocs de messages asynchrones, consultez Comment : spécifier des stratégies de planificateur.

Cette rubrique utilise les fonctionnalités de la Bibliothèque d'agents asynchrones, telles que les agents, les blocs de messages et les fonctions de passage de messages, pour exécuter un travail. Pour plus d'informations sur la bibliothèque d'agents asynchrones, consultez Bibliothèque d'agents asynchrones.

Exemple

L'exemple suivant définit deux classes qui dérivent de concurrency::agent: permutor et de printer La classe permutor calcule toutes les permutations d'une chaîne d'entrée donnée. La classe printer imprime les messages de progression sur la console. La classe permutor effectue une opération nécessitant de nombreuses ressources de calcul et qui risque d'utiliser toutes les ressources disponibles. Pour être utile, la classe printer doit imprimer chaque message de progression en temps opportun.

Pour fournir à la classe printer un accès équitable aux ressources de calcul, cet exemple utilise les étapes décrites dans Comment : gérer une instance de planificateur pour créer une instance du planificateur ayant une stratégie personnalisée. La stratégie personnalisée affecte comme priorité de thread la classe de priorité la plus élevée.

Pour illustrer les avantages offerts par l'utilisation d'un planificateur ayant une stratégie personnalisée, cet exemple effectue la tâche totale à deux reprises. L'exemple utilise tout d'abord le planificateur par défaut pour planifier les deux tâches. L'exemple utilise ensuite le planificateur par défaut pour planifier l'objet permutor, et un planificateur ayant une stratégie personnalisée pour planifier l'objet printer.

// permute-strings.cpp 
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <agents.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// Computes all permutations of a given input string. 
class permutor : public agent
{
public:
   explicit permutor(ISource<wstring>& source,
      ITarget<unsigned int>& progress)
      : _source(source)
      , _progress(progress)
   {
   }

   explicit permutor(ISource<wstring>& source,
      ITarget<unsigned int>& progress,
      Scheduler& scheduler)
      : agent(scheduler)
      , _source(source)
      , _progress(progress)
   {
   }

   explicit permutor(ISource<wstring>& source,
      ITarget<unsigned int>& progress,
      ScheduleGroup& group)
      : agent(group)       
      , _source(source)
      , _progress(progress)
   {
   }

protected:
   // Performs the work of the agent. 
   void run()
   {
      // Read the source string from the buffer.
      wstring s = receive(_source);

      // Compute all permutations.
      permute(s);

      // Set the status of the agent to agent_done.
      done();
   }

   // Computes the factorial of the given value. 
   unsigned int factorial(unsigned int n)
   {
      if (n == 0)
         return 0;
      if (n == 1)
         return 1;
      return n * factorial(n - 1);
   }

   // Computes the nth permutation of the given wstring.
   wstring permutation(int n, const wstring& s) 
   {
      wstring t(s);

      size_t len = t.length();
      for (unsigned int i = 2; i < len; ++i)
      {
         swap(t[n % i], t[i]);
         n = n / i;
      }
      return t;
   }

   // Computes all permutations of the given string. 
   void permute(const wstring& s)
   {      
      // The factorial gives us the number of permutations. 
      unsigned int permutation_count = factorial(s.length());

      // The number of computed permutations.
      LONG count = 0L;      

      // Tracks the previous percentage so that we only send the percentage 
      // when it changes. 
      unsigned int previous_percent = 0u;

      // Send initial progress message.
      send(_progress, previous_percent);

      // Compute all permutations in parallel.
      parallel_for (0u, permutation_count, [&](unsigned int i) {
         // Compute the permutation.
         permutation(i, s);

         // Send the updated status to the progress reader. 
         unsigned int percent = 100 * InterlockedIncrement(&count) / permutation_count;
         if (percent > previous_percent)
         {
             send(_progress, percent);
             previous_percent = percent;
         }
      });

      // Send final progress message.
      send(_progress, 100u);
   }

private:
   // The buffer that contains the source string to permute.
   ISource<wstring>& _source;

   // The buffer to write progress status to.
   ITarget<unsigned int>& _progress;
};

// Prints progress messages to the console. 
class printer : public agent
{
public:
   explicit printer(ISource<wstring>& source,
      ISource<unsigned int>& progress)
      : _source(source)
      , _progress(progress)
   {
   }

   explicit printer(ISource<wstring>& source,
      ISource<unsigned int>& progress, Scheduler& scheduler)
      : agent(scheduler)
      , _source(source)
      , _progress(progress)
   {
   }

   explicit printer(ISource<wstring>& source,
      ISource<unsigned int>& progress, ScheduleGroup& group)
      : agent(group)       
      , _source(source)
      , _progress(progress)
   {
   }

protected:
   // Performs the work of the agent. 
   void run()
   {
      // Read the source string from the buffer and print a message.
      wstringstream ss;
      ss << L"Computing all permutations of '" << receive(_source) << L"'..." << endl;
      wcout << ss.str();

      // Print each progress message. 
      unsigned int previous_progress = 0u;
      while (true)
      {         
         unsigned int progress = receive(_progress);

         if (progress > previous_progress || progress == 0u)
         { 
            wstringstream ss;
            ss << L'\r' << progress << L"% complete...";
            wcout << ss.str();
            previous_progress = progress;
         }

         if (progress == 100)
            break;
      }
      wcout << endl;

      // Set the status of the agent to agent_done.
      done();
   }

private:
   // The buffer that contains the source string to permute.
   ISource<wstring>& _source;

   // The buffer that contains progress status.
   ISource<unsigned int>& _progress;
};

// Computes all permutations of the given string.  
void permute_string(const wstring& source,
   Scheduler& permutor_scheduler, Scheduler& printer_scheduler)
{  
   // Message buffer that contains the source string. 
   // The permutor and printer agents both read from this buffer.
   single_assignment<wstring> source_string;

   // Message buffer that contains the progress status. 
   // The permutor agent writes to this buffer and the printer agent reads 
   // from this buffer.
   unbounded_buffer<unsigned int> progress;

   // Create the agents with the appropriate schedulers.
   permutor agent1(source_string, progress, permutor_scheduler);
   printer agent2(source_string, progress, printer_scheduler);

   // Start the agents.
   agent1.start();
   agent2.start();

   // Write the source string to the message buffer. This will unblock the agents.
   send(source_string, source);

   // Wait for both agents to finish.
   agent::wait(&agent1);
   agent::wait(&agent2);
}

int wmain()
{
   const wstring source(L"Grapefruit");

   // Compute all permutations on the default scheduler.

   Scheduler* default_scheduler = CurrentScheduler::Get();

   wcout << L"With default scheduler: " << endl;
   permute_string(source, *default_scheduler, *default_scheduler);
   wcout << endl;

   // Compute all permutations again. This time, provide a scheduler that 
   // has higher context priority to the printer agent.

   SchedulerPolicy printer_policy(1, ContextPriority, THREAD_PRIORITY_HIGHEST);
   Scheduler* printer_scheduler = Scheduler::Create(printer_policy);

   // Register to be notified when the scheduler shuts down.
   HANDLE hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
   printer_scheduler->RegisterShutdownEvent(hShutdownEvent);

   wcout << L"With higher context priority: " << endl;
   permute_string(source, *default_scheduler, *printer_scheduler);
   wcout << endl; 

   // Release the printer scheduler.
   printer_scheduler->Release();

   // Wait for the scheduler to shut down and destroy itself.
   WaitForSingleObject(hShutdownEvent, INFINITE);

   // Close the event handle.
   CloseHandle(hShutdownEvent);
}

Cet exemple génère la sortie suivante.

  

Bien que les deux ensembles de tâches produisent le même résultat, la version qui utilise une stratégie personnalisée permet à l'objet printer de s'exécuter avec une priorité élevée et donc une meilleure réactivité.

Compilation du code

Copiez l'exemple de code et collez-le dans un projet Visual Studio, ou collez-le dans un fichier nommé permute-strings.cpp puis exécutez la commande suivante dans une fenêtre d'invite de commandes Visual Studio.

cl.exe /EHsc permute-strings.cpp

Voir aussi

Référence

How-to: Specify Specific Scheduler Policies

Concepts

Stratégies de planificateur

Agents asynchrones