Cómo: Crear una tarea que se complete después de un retardo
Este ejemplo muestra cómo utilizar concurrency::task, concurrency::cancellation_token_source, concurrency::cancellation_token, concurrency::task_completion_event, concurrency::timer, y las clases de concurrency::call para crear una tarea completar después de un retraso. Puede utilizar este método para crear bucles que sondean en ocasiones para los datos, presenta los tiempos de espera, el control de retraso de datos proporcionados por el usuario durante un tiempo predeterminado, etc.
Ejemplo
En el ejemplo siguiente se muestran las funciones complete_after y cancel_after_timeout. La función de complete_after crea un objeto de task completar después de que el retraso especificado. Usa un objeto de timer y un objeto de call para establecer un objeto de task_completion_event después de que el retraso especificado. Usar la clase de task_completion_event , puede definir una tarea completar después de un subproceso u otras señales de la tarea que un valor está disponible. Cuando se establece el evento, las tareas de agente de escucha completadas y sus continuaciones se han programado para ejecutarse.
Sugerencia |
---|
Para obtener más información sobre las clases de timer y de call , que forman parte de la biblioteca de agentes asincrónicos, vea Bloques de mensajes asincrónicos. |
Las compilaciones de la función de cancel_after_timeoutcomplete_after funcionan para cancelar una tarea si esa tarea no completa antes de un tiempo de espera especificado. La función de cancel_after_timeout crea dos tareas. La primera tarea indica correctamente y se completa después de que la tarea proporcionada finalice; la segunda tarea indica error y lo completa después de tiempo de espera especificado. La función de cancel_after_timeout crea una tarea de continuación que se ejecute cuando la tarea correcta o errónea completa. Si la tarea errónea completa, la continuación cancela el origen de token para cancelar la tarea global.
// 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;
});
}
El ejemplo siguiente se calcula el contador de números primos en varias veces el intervalo [0, 100000]. Se produce un error en la operación si no se completa en un límite de tiempo determinado. La función de count_primes muestra cómo utilizar la función de cancel_after_timeout . Cuenta el número de prepara en el intervalo especificado y no se supera si la tarea no se completa en el tiempo proporcionado. Llamadas de función wmain que el múltiplo de la función de count_primes mide el tiempo. Cada vez, parte en dos el límite de tiempo. El programa termina después de que la operación no se complete en el límite de la hora actual.
// 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.
*/
Cuando se utiliza esta técnica de cancelar tareas después de un intervalo, ninguna tarea sin iniciar no se inicia después de que la tarea global se cancela. Sin embargo, es importante que cualquier tarea de ejecución prolongada responder a la cancelación a tiempo. En este ejemplo, las llamadas al método de count_primes las funciones de concurrency::is_task_cancellation_requested y de cancel_current_task a responder a la cancelación. O bien, puede llamar a la función de concurrency::interruption_point ). Para obtener más información sobre la cancelación de tareas, vea Cancelación en la biblioteca PPL.
A continuación se muestra el código completo de este ejemplo:
// 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.
*/
Compilar el código
Para compilar el código, cópielo y péguelo en un proyecto de Visual Studio, o péguelo en un archivo denominado task-delay.cpp y después se ejecute el siguiente comando en una ventana de símbolo del sistema de Visual Studio.
cl.exe /EHsc task-delay.cpp
Vea también
Referencia
task (Clase) (Motor en tiempo de ejecución de simultaneidad)
cancellation_token_source (Clase)
is_task_cancellation_requested (Función)
Conceptos
Paralelismo de tareas (Runtime de simultaneidad)