Freigeben über


Vorgehensweise: Erstellen eines Tasks, der nach einer Verzögerung abgeschlossen wird

In diesem Beispiel wird gezeigt, wie Sie mithilfe der concurrency::taskKlassen , concurrency::cancellation_token_source, concurrency::call concurrency::cancellation_tokenconcurrency::task_completion_eventconcurrency::timerdie 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