Comment : spécifier des stratégies de planificateur
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 indique comment utiliser une stratégie du planificateur pour augmenter la priorité de thread d'une tâche qui imprime un indicateur de progression sur la console.
Pour obtenir un exemple qui utilise des stratégies du planificateur personnalisées avec des agents asynchrones, consultez Comment : créer des agents qui utilisent des stratégies de planificateur spécifiques.
Exemple
L'exemple suivant effectue deux tâches en parallèle. La première tâche calcule le nème nombre de Fibonacci. La deuxième tâche imprime un indicateur de progression sur la console.
La première tâche utilise la décomposition récursive pour calculer le nombre de Fibonacci. Autrement dit, chaque tâche crée de manière récursive des sous-tâches pour calculer le résultat total. Une tâche qui utilise la décomposition récursive peut utiliser toutes les ressources disponibles, et par conséquent priver d'autres tâches de temps processeur. Dans cet exemple, la tâche qui imprime l'indicateur de progression risque de ne pas disposer d'un accès en temps voulu aux ressources de calcul.
Pour que la tâche qui imprime un message de progression bénéficie d'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.
Cet exemple utilise les classes concurrency::call et concurrency::timer pour imprimer l'indicateur de progression. Ces classes ont des versions de leurs constructeurs qui prennent une référence à un objet concurrency::Scheduler qui les planifie. L'exemple utilise le planificateur par défaut pour planifier la tâche qui calcule le nombre de Fibonacci et l'instance du planificateur pour planifier la tâche qui imprime l'indicateur de progression.
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 la première tâche, et un planificateur ayant une stratégie personnalisée pour planifier la deuxième tâche.
// scheduler-policy.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Computes the nth Fibonacci number.
// This function illustrates a lengthy operation and is therefore
// not optimized for performance.
int fibonacci(int n)
{
if (n < 2)
return n;
// Compute the components in parallel.
int n1, n2;
parallel_invoke(
[n,&n1] { n1 = fibonacci(n-1); },
[n,&n2] { n2 = fibonacci(n-2); }
);
return n1 + n2;
}
// Prints a progress indicator while computing the nth Fibonacci number.
void fibonacci_with_progress(Scheduler& progress_scheduler, int n)
{
// Use a task group to compute the Fibonacci number.
// The tasks in this group are scheduled by the current scheduler.
structured_task_group tasks;
auto task = make_task([n] {
fibonacci(n);
});
tasks.run(task);
// Create a call object that prints its input to the console.
// This example uses the provided scheduler to schedule the
// task that the call object performs.
call<wchar_t> c(progress_scheduler, [](wchar_t c) {
wcout << c;
});
// Connect the call object to a timer object. The timer object
// sends a progress message to the call object every 100 ms.
// This example also uses the provided scheduler to schedule the
// task that the timer object performs.
timer<wchar_t> t(progress_scheduler, 100, L'.', &c, true);
t.start();
// Wait for the task that computes the Fibonacci number to finish.
tasks.wait();
// Stop the timer.
t.stop();
wcout << L"done" << endl;
}
int wmain()
{
// Calculate the 38th Fibonacci number.
const int n = 38;
// Use the default scheduler to schedule the progress indicator while
// the Fibonacci number is calculated in the background.
wcout << L"Default scheduler:" << endl;
fibonacci_with_progress(*CurrentScheduler::Get(), n);
// Now use a scheduler that has a custom policy for the progress indicator.
// The custom policy specifies the thread priority to the highest
// priority class.
SchedulerPolicy policy(1, ContextPriority, THREAD_PRIORITY_HIGHEST);
Scheduler* scheduler = Scheduler::Create(policy);
// Register to be notified when the scheduler shuts down.
HANDLE hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
scheduler->RegisterShutdownEvent(hShutdownEvent);
wcout << L"Scheduler that has a custom policy:" << endl;
fibonacci_with_progress(*scheduler, n);
// Release the final reference to the scheduler. This causes the scheduler
// to shut down.
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 jeux de tâches produisent le même résultat, la version qui utilise une stratégie personnalisée permet à la tâche qui imprime l'indicateur de progression 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é scheduler-policy.cpp puis exécutez la commande suivante dans une fenêtre d'invite de commandes Visual Studio.
cl.exe /EHsc scheduler-policy.cpp
Voir aussi
Tâches
Comment : gérer une instance de planificateur
Comment : créer des agents qui utilisent des stratégies de planificateur spécifiques