Vorgehensweise: Erstellen eines Tasks, der nach einer Verzögerung abgeschlossen wird
In diesem Beispiel wird gezeigt, wie Sie mithilfe der concurrency::task
Klassen , concurrency::cancellation_token_source
, concurrency::call
concurrency::cancellation_token
concurrency::task_completion_event
concurrency::timer
die nach einer Verzögerung abgeschlossene Aufgabe erstellen. Sie können diese Methode verwenden, um Schleifen zu erstellen, die gelegentlich Daten abfragen. Sie können auch Timeouts, verzögerte Behandlung von Benutzereingaben für eine vordefinierte Zeit usw. einführen.
Beispiel: complete_after- und cancel_after_timeout-Funktionen
Im folgenden Beispiel werden die Funktionen complete_after
und cancel_after_timeout
dargestellt. Die complete_after
Funktion erstellt ein task
Objekt, das nach der angegebenen Verzögerung abgeschlossen wird. Es verwendet ein timer
Objekt und ein call
Objekt, um ein task_completion_event
Objekt nach der angegebenen Verzögerung festzulegen. Mithilfe der task_completion_event
Klasse können Sie eine Aufgabe definieren, die nach einem Thread oder einem anderen Vorgang abgeschlossen wird, um zu signalisieren, dass ein Wert verfügbar ist. Wenn das Ereignis festgelegt ist, werden Listeneraufgaben abgeschlossen, und deren Fortsetzungen werden für die Ausführung geplant.
Tipp
Weitere Informationen zu den timer
Klassen und call
Klassen, die Teil der asynchronen Agents-Bibliothek sind, finden Sie unter "Asynchrone Nachrichtenblöcke".
Die cancel_after_timeout
Funktion baut auf der complete_after
Funktion auf, um eine Aufgabe abzubrechen, wenn diese Aufgabe nicht vor einem bestimmten Timeout abgeschlossen wird. Die cancel_after_timeout
Funktion erstellt zwei Aufgaben. Der erste Vorgang gibt erfolg- und abgeschlossen an, nachdem die bereitgestellte Aufgabe abgeschlossen wurde. Der zweite Vorgang gibt einen Fehler an und wird nach dem angegebenen Timeout abgeschlossen. Die cancel_after_timeout
Funktion erstellt eine Fortsetzungsaufgabe, die ausgeführt wird, wenn die Erfolgs- oder Fehleraufgabe abgeschlossen ist. Wenn die Fehleraufgabe zuerst abgeschlossen ist, bricht die Fortsetzung die Tokenquelle ab, um den Gesamtvorgang abzubrechen.
// 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;
});
}
Beispiel: Berechnen der Anzahl der Primzahlen
Im folgenden Beispiel wird die Anzahl der Primzahlen im Bereich [0, 100000] mehrmals berechnet. Der Vorgang schlägt fehl, wenn er in einem bestimmten Zeitlimit nicht abgeschlossen wird. Die count_primes
Funktion veranschaulicht die Verwendung der cancel_after_timeout
Funktion. Es zählt die Anzahl der Primes im angegebenen Bereich und schlägt fehl, wenn der Vorgang nicht in der angegebenen Zeit abgeschlossen wird. Die wmain
Funktion ruft die count_primes
Funktion mehrmals auf. Jedes Mal halbiert es das Zeitlimit. Das Programm endet, nachdem der Vorgang nicht im aktuellen Zeitlimit abgeschlossen wurde.
// 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.
*/
Wenn Sie diese Technik verwenden, um Vorgänge nach einer Verzögerung abzubrechen, werden alle nicht gestarteten Vorgänge nicht gestartet, nachdem der gesamte Vorgang abgebrochen wurde. Es ist jedoch wichtig, dass lange ausgeführte Aufgaben schnell auf den Abbruch reagieren. Weitere Informationen zum Abbruch von Vorgängen finden Sie unter "Abbruch" in der PPL.
Vollständiges Codebeispiel
Hier ist der vollständige Code für dieses Beispiel:
// 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.
*/
Kompilieren des Codes
Um den Code zu kompilieren, kopieren Sie ihn, und fügen Sie ihn dann in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei ein, die benannt task-delay.cpp
ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus.
cl.exe /EHsc task-delay.cpp
Siehe auch
Task-Parallelität
task-Klasse (Concurrency Runtime)
cancellation_token_source-Klasse
cancellation_token-Klasse
task_completion_event-Klasse
timer-Klasse
call-Klasse
Asynchrone Nachrichtenblöcke
Abbruch in der PPL