Freigeben über


Gewusst wie: Erstellen einer Aufgabe, die nach einer Verzögerung abgeschlossen wird

Dieses Beispiel zeigt, wie concurrency::task, concurrency::cancellation_token_source, concurrency::cancellation_token, concurrency::task_completion_event, concurrency::timer und concurrency::call-Klassen verwendet, um eine Aufgabe zu erstellen, die nach einer Verzögerung beendet. Sie können diese Methode verwenden, um zu Schleifen erstellen, die gelegentlich für Daten abrufen, Timeout vorstellen, Verzögerungsbehandlung der Benutzereingaben während einer vorgegebenen Zeit, z. B.

Beispiel

Im folgenden Beispiel werden die Funktionen complete_after und cancel_after_timeout dargestellt. Die complete_after-Funktion erstellt ein task-Objekt, nachdem das die angegebene Verzögerung beendet. Sie verwendet ein timer-Objekt und ein call-Objekt, ein task_completion_event-Objekt festzulegen nachdem die angegebene Verzögerung. Mithilfe der task_completion_event-Klasse verwenden, können Sie eine Aufgabe definieren, die abgeschlossen, nachdem ein gegebener Thread oder eine andere Aufgabe signalisiert, dass ein Wert verfügbar ist. Wenn das Ereignis festgelegt wird, schließen Listeneraufgaben ab und ihre Fortsetzungen geplant werden, die ausgeführt wird.

Tipp

Weitere Informationen über die timer und call-Klassen, die ein Teil der Asynchronous Agents sind, finden Sie unter Asynchrone Nachrichtenblöcke.

Die cancel_after_timeout-Funktionsbuilds auf complete_after arbeiten, um eine Aufgabe abzubrechen, wenn diese Aufgabe nicht vor einem angegebenen Timeout. Die Funktion cancel_after_timeout erstellt zwei Aufgaben. Die erste Aufgabe wird Erfolg an und werden abgeschlossen, nachdem die bereitgestellte Aufgabe ausführt; die zweite Aufgabe gibt Fehler an und enthält nach dem angegebenen Timeout ab. Die cancel_after_timeout-Funktion erstellt eine Fortsetzungsaufgabe, die ausgeführt wird, wenn die Fehleraufgabe Erfolgs- oder abgeschlossen wurde. Wenn die Fehleraufgabe zuerst abgeschlossen wurde, wird die Fortsetzung die Tokenquelle ab, um die gesamte Aufgabe 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;
    });
}

Im folgenden Beispiel wird die Anzahl der Primzahlen im Bereich [0, 100000] mehrmals. Der Vorgang schlägt fehl, wenn er nicht in einer Grenzzeit des bestimmten Zeitraum abgeschlossen wird. Die count_primes-Funktion zeigt, wie die cancel_after_timeout-Funktion verwendet. Sie wird die Zahl von vorbereitet im angegebenen Bereich und schlägt fehl, wenn die Aufgabe nicht in der angegebenen Zeit ausgeführt wird. Die wmain-Funktion die count_primes-Funktion mehrmals. Immer halbiert das Zeitlimit. Das Programm beendet, nachdem der Vorgang nicht im aktuellen Zeitlimits abschließ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.
*/

Wenn Sie diese Technik anwenden, Aufgaben abzubrechen, nachdem eine Verzögerung, keine unstarted Aufgaben nicht gestartet, nachdem die gesamte Aufgabe abgebrochen wird. Es ist jedoch wichtig, dass alle Aufgaben mit langer Laufzeit auf Abbrüche rechtzeitig antworten. In diesem Beispiel die count_primes-Methode ruft die concurrency::is_task_cancellation_requestedcancel_current_task und Funktionen, die auf den Abbruch reagieren. (Alternativ, können Sie die concurrency::interruption_point-Funktion aufrufen). Weitere Informationen zum Aufgabenabbruch finden Sie unter Abbruch in der PPL.

Dies ist der vollständige Code für dieses Beispiel:

// 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.
*/

Kompilieren des Codes

Zum Kompilieren kopieren Sie den Code, und in einem Visual Studio-Projekt dann, oder fügen Sie ihn in eine Datei mit dem Namen task-delay.cpp ein, und dann folgenden Befehl in einem Visual Studio-Eingabeaufforderung ausgeführt.

cl.exe /EHsc task-delay.cpp

Siehe auch

Referenz

task-Klasse (Concurrency Runtime)

cancellation_token_source-Klasse

cancellation_token-Klasse

task_completion_event-Klasse

is_task_cancellation_requested-Funktion

cancel_current_task-Funktion

interruption_point-Funktion

timer-Klasse

call-Klasse

Konzepte

Aufgabenparallelität (Concurrency Runtime)

Asynchrone Nachrichtenblöcke

Abbruch in der PPL