Comment : créer une tâche qui se termine après un certain délai.
Cet exemple montre comment utiliser concurrency::task, concurrency::cancellation_token_source, concurrency::cancellation_token, concurrency::task_completion_event, concurrency::timer, les classes et d' concurrency::call pour créer une tâche qui se termine après un délai.Vous pouvez utiliser cette méthode pour générer des boucles qui votent parfois des données, tapez délais d'attente, gestion de délai d'entrée d'utilisateur pendant une durée prédéterminé, et ainsi de suite.
Exemple
L'exemple suivant illustre les fonctions complete_after et cancel_after_timeout.La fonction d' complete_after crée un objet d' task qui se termine après que le délai spécifié.Elle utilise un objet d' timer et un objet d' call pour définir un objet d' task_completion_event après que le délai spécifié.À l'aide de la classe d' task_completion_event, vous pouvez définir une tâche qui se termine une fois qu'un thread ou une tâche différente signale qu'une valeur est disponible.Lorsque l'événement est défini, les tâches d'écouteur terminées et leurs suites sont planifiées pour fonctionner.
Conseil |
---|
Pour plus d'informations sur les classes d' timer et d' call, qui font partie de la bibliothèque d'agents asynchrones, consultez l' Blocs de messages asynchrones. |
Les builds de fonction d' cancel_after_timeout sur complete_after fonctionnent pour annuler une tâche si cette tâche ne se termine pas avant un délai d'attente donné.La fonction d' cancel_after_timeout crée deux tâches.La première tâche indique le succès et se termine lorsque la tâche fournie se termine ; la deuxième tâche indique l'échec et se termine après le délai d'attente spécifié.La fonction d' cancel_after_timeout crée une tâche de continuation qui s'exécute lorsque la tâche de succès ou l'échec se termine.Si la tâche de défaillance se termine d'abord, la continuation annule la source de jeton pour annuler la tâche globale.
// Creates a task that completes after the specified delay.
task<void> complete_after(unsigned int timeout)
{
// A task completion event that is set when a timer fires.
task_completion_event<void> tce;
// Create a non-repeating timer.
auto fire_once = new timer<int>(timeout, 0, nullptr, false);
// Create a call object that sets the completion event after the timer fires.
auto callback = new call<int>([tce](int)
{
tce.set();
});
// Connect the timer to the callback and start the timer.
fire_once->link_target(callback);
fire_once->start();
// Create a task that completes after the completion event is set.
task<void> event_set(tce);
// Create a continuation task that cleans up resources and
// and return that continuation task.
return event_set.then([callback, fire_once]()
{
delete callback;
delete fire_once;
});
}
// Cancels the provided task after the specifed delay, if the task
// did not complete.
template<typename T>
task<T> cancel_after_timeout(task<T> t, cancellation_token_source cts, unsigned int timeout)
{
// Create a task that returns true after the specified task completes.
task<bool> success_task = t.then([](T)
{
return true;
});
// Create a task that returns false after the specified timeout.
task<bool> failure_task = complete_after(timeout).then([]
{
return false;
});
// Create a continuation task that cancels the overall task
// if the timeout task finishes first.
return (failure_task || success_task).then([t, cts](bool success)
{
if(!success)
{
// Set the cancellation token. The task that is passed as the
// t parameter should respond to the cancellation and stop
// as soon as it can.
cts.cancel();
}
// Return the original task.
return t;
});
}
L'exemple suivant calcule la quantité de nombres premiers dans la plage [0, 100000] plusieurs fois.L'opération échoue si elle ne se termine pas dans un délai de temps donné.La fonction d' count_primes montre comment utiliser la fonction d' cancel_after_timeout .Elle compte le nombre de amorce dans l'intervalle donné et échoue si la tâche ne se termine pas dans le temps disponible.Les appels de fonction d' wmain qu' count_primes s'exécutent plusieurs fois.Chaque fois, il divise en deux la limite de temps.Le programme se termine après que l'opération ne se termine pas dans la limite de temps actuelle.
// Determines whether the input value is prime.
bool is_prime(int n)
{
if (n < 2)
return false;
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
return false;
}
return true;
}
// Counts the number of primes in the range [0, max_value].
// The operation fails if it exceeds the specified timeout.
bool count_primes(unsigned int max_value, unsigned int timeout)
{
cancellation_token_source cts;
// Create a task that computes the count of prime numbers.
// The task is canceled after the specified timeout.
auto t = cancel_after_timeout(task<size_t>([max_value, timeout]
{
combinable<size_t> counts;
parallel_for<unsigned int>(0, max_value + 1, [&counts](unsigned int n)
{
// Respond if the overall task is cancelled by canceling
// the current task.
if (is_task_cancellation_requested())
{
cancel_current_task();
}
// NOTE: You can replace the calls to is_task_cancellation_requested
// and cancel_current_task with a call to interruption_point.
// interruption_point();
// Increment the local counter if the value is prime.
if (is_prime(n))
{
counts.local()++;
}
});
// Return the sum of counts across all threads.
return counts.combine(plus<size_t>());
}, cts.get_token()), cts, timeout);
// Print the result.
try
{
auto primes = t.get();
wcout << L"Found " << primes << L" prime numbers within "
<< timeout << L" ms." << endl;
return true;
}
catch (const task_canceled& e)
{
wcout << L"The task timed out." << endl;
return false;
}
}
int wmain()
{
// Compute the count of prime numbers in the range [0, 100000]
// until the operation fails.
// Each time the test succeeds, the time limit is halved.
unsigned int max = 100000;
unsigned int timeout = 5000;
bool success = true;
do
{
success = count_primes(max, timeout);
timeout /= 2;
} while (success);
}
/* Sample output:
Found 9592 prime numbers within 5000 ms.
Found 9592 prime numbers within 2500 ms.
Found 9592 prime numbers within 1250 ms.
Found 9592 prime numbers within 625 ms.
The task timed out.
*/
Lorsque vous utilisez cette technique pour annuler des tâches après un délai, aucune tâche unstarted ne démarre pas une fois la tâche globale est annulée.Toutefois, il est important que toutes les tâches de longue durée répondent à l'annulation en temps voulu.Dans cet exemple, les appels de méthode d' count_primes les fonctions d' concurrency::is_task_cancellation_requested et d' cancel_current_task à répondre à l'annulation.(Sinon, vous pouvez appeler la fonction d' concurrency::interruption_point ).Pour plus d'informations sur l'annulation de tâche, consultez Annulation dans la bibliothèque de modèles parallèles.
Voici le code complet pour cet exemple :
// task-delay.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Creates a task that completes after the specified delay.
task<void> complete_after(unsigned int timeout)
{
// A task completion event that is set when a timer fires.
task_completion_event<void> tce;
// Create a non-repeating timer.
auto fire_once = new timer<int>(timeout, 0, nullptr, false);
// Create a call object that sets the completion event after the timer fires.
auto callback = new call<int>([tce](int)
{
tce.set();
});
// Connect the timer to the callback and start the timer.
fire_once->link_target(callback);
fire_once->start();
// Create a task that completes after the completion event is set.
task<void> event_set(tce);
// Create a continuation task that cleans up resources and
// and return that continuation task.
return event_set.then([callback, fire_once]()
{
delete callback;
delete fire_once;
});
}
// Cancels the provided task after the specifed delay, if the task
// did not complete.
template<typename T>
task<T> cancel_after_timeout(task<T> t, cancellation_token_source cts, unsigned int timeout)
{
// Create a task that returns true after the specified task completes.
task<bool> success_task = t.then([](T)
{
return true;
});
// Create a task that returns false after the specified timeout.
task<bool> failure_task = complete_after(timeout).then([]
{
return false;
});
// Create a continuation task that cancels the overall task
// if the timeout task finishes first.
return (failure_task || success_task).then([t, cts](bool success)
{
if(!success)
{
// Set the cancellation token. The task that is passed as the
// t parameter should respond to the cancellation and stop
// as soon as it can.
cts.cancel();
}
// Return the original task.
return t;
});
}
// Determines whether the input value is prime.
bool is_prime(int n)
{
if (n < 2)
return false;
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
return false;
}
return true;
}
// Counts the number of primes in the range [0, max_value].
// The operation fails if it exceeds the specified timeout.
bool count_primes(unsigned int max_value, unsigned int timeout)
{
cancellation_token_source cts;
// Create a task that computes the count of prime numbers.
// The task is canceled after the specified timeout.
auto t = cancel_after_timeout(task<size_t>([max_value, timeout]
{
combinable<size_t> counts;
parallel_for<unsigned int>(0, max_value + 1, [&counts](unsigned int n)
{
// Respond if the overall task is cancelled by canceling
// the current task.
if (is_task_cancellation_requested())
{
cancel_current_task();
}
// NOTE: You can replace the calls to is_task_cancellation_requested
// and cancel_current_task with a call to interruption_point.
// interruption_point();
// Increment the local counter if the value is prime.
if (is_prime(n))
{
counts.local()++;
}
});
// Return the sum of counts across all threads.
return counts.combine(plus<size_t>());
}, cts.get_token()), cts, timeout);
// Print the result.
try
{
auto primes = t.get();
wcout << L"Found " << primes << L" prime numbers within "
<< timeout << L" ms." << endl;
return true;
}
catch (const task_canceled& e)
{
wcout << L"The task timed out." << endl;
return false;
}
}
int wmain()
{
// Compute the count of prime numbers in the range [0, 100000]
// until the operation fails.
// Each time the test succeeds, the time limit is halved.
unsigned int max = 100000;
unsigned int timeout = 5000;
bool success = true;
do
{
success = count_primes(max, timeout);
timeout /= 2;
} while (success);
}
/* Sample output:
Found 9592 prime numbers within 5000 ms.
Found 9592 prime numbers within 2500 ms.
Found 9592 prime numbers within 1250 ms.
Found 9592 prime numbers within 625 ms.
The task timed out.
*/
Compilation du code
Pour compiler le code, copiez-le puis collez-le dans un projet Visual Studio, ou collez-le dans un fichier nommé task-delay.cpp puis exécutez la commande suivante dans une fenêtre d'invite de commandes de Visual Studio.
cl.exe /EHsc task-delay.cpp
Voir aussi
Référence
task (Concurrency Runtime), classe
cancellation_token_source, classe
is_task_cancellation_requested, fonction