Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este exemplo mostra como usar as classes concurrency::task, concurrency::cancellation_token_source, concurrency::cancellation_token, concurrency::task_completion_event, concurrency::timer e concurrency::call para criar uma tarefa que conclui após um atraso. Você pode usar este método para criar loops que ocasionalmente interrogam dados. Você também pode introduzir timeouts, atrasar o processamento da entrada do utilizador por um tempo predeterminado, e assim por diante.
Exemplo: funções complete_after e cancel_after_timeout
O exemplo a seguir mostra as funções complete_after e cancel_after_timeout. A complete_after função cria um task objeto que é concluído após o atraso especificado. Ele usa um timer objeto e um call objeto para definir um task_completion_event objeto após o atraso especificado. Usando a task_completion_event classe, você pode definir uma tarefa que é concluída depois que um thread ou outra tarefa sinaliza que um valor está disponível. Quando o evento é definido, as tarefas do ouvinte são concluídas e suas continuações são agendadas para serem executadas.
Sugestão
Para obter mais informações sobre as timer classes e call , que fazem parte da Biblioteca de Agentes Assíncronos, consulte Blocos de mensagens assíncronas.
A cancel_after_timeout função baseia-se na complete_after função para cancelar uma tarefa se essa tarefa não for concluída antes de um determinado tempo limite. A cancel_after_timeout função cria duas tarefas. A primeira tarefa indica sucesso e é concluída após a conclusão da tarefa fornecida. A segunda tarefa indica falha e é concluída após o tempo limite especificado. A cancel_after_timeout função cria uma tarefa de continuação que é executada quando a tarefa de sucesso ou falha é concluída. Se a tarefa de falha for concluída primeiro, a continuação cancelará a origem do token para cancelar a tarefa geral.
// 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;
});
}
Exemplo: Contar números primos
O exemplo a seguir calcula a contagem de números primos no intervalo [0, 100000] várias vezes. A operação falhará se não for concluída em um determinado período de tempo. A count_primes função demonstra como usar a cancel_after_timeout função. Ele conta o número de primos no intervalo determinado e falha se a tarefa não for concluída no tempo fornecido. A wmain função chama a count_primes função várias vezes. De cada vez, reduz para metade o prazo. O programa termina depois que a operação não é concluída no limite de tempo atual.
// 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, cts]
{
combinable<size_t> counts;
parallel_for<unsigned int>(0, max_value + 1, [&counts, cts](unsigned int n)
{
// Respond if the overall task is cancelled by canceling
// the current task.
if (cts.get_token().is_canceled())
{
cancel_current_task();
}
// NOTE: You can replace the calls to is_canceled
// 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&)
{
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.
*/
Quando você usa essa técnica para cancelar tarefas após um atraso, as tarefas não iniciadas não serão iniciadas depois que a tarefa geral for cancelada. No entanto, é importante que qualquer tarefa de longa duração responda rapidamente ao cancelamento. Para obter mais informações sobre o cancelamento de tarefas, consulte Cancelamento no PPL.
Exemplo de código completo
Aqui está o código completo para este exemplo:
// task-delay.cpp
// compile with: /EHsc
#include <ppl.h>
#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, cts]
{
combinable<size_t> counts;
parallel_for<unsigned int>(0, max_value + 1, [&counts, cts](unsigned int n)
{
// Respond if the overall task is cancelled by canceling
// the current task.
if (cts.get_token().is_canceled())
{
cancel_current_task();
}
// NOTE: You can replace the calls to is_canceled
// 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&)
{
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.
*/
Compilando o código
Para compilar o código, copie-o e cole-o em um projeto do Visual Studio ou cole-o em um arquivo nomeado task-delay.cpp e, em seguida, execute o seguinte comando em uma janela do prompt de comando do Visual Studio.
cl.exe /EHsc task-delay.cpp
Ver também
Paralelismo de tarefas
Classe de tarefa (Concurrency Runtime)
cancellation_token_source Classe
cancellation_token Classe
Classe Evento de Conclusão de Tarefa
Classe do temporizador
chamar Classe
Blocos de mensagens assíncronas
Cancelamento no PPL