Megosztás:


Útmutató: Késés után befejezett feladat létrehozása

Ez a példa bemutatja, hogyan hozhat létre késleltetés után befejezett feladatot a concurrency::task, concurrency::cancellation_token_source, concurrency::cancellation_token, , concurrency::task_completion_eventconcurrency::timerés concurrency::call osztályokkal. Ezzel a módszerrel olyan hurkokat hozhat létre, amelyek időnként lekérdezik az adatokat. Emellett időtúllépéseket is bevezethet, késleltetheti a felhasználói bemenetek kezelését egy előre meghatározott ideig, és így tovább.

Példa: complete_after és cancel_after_timeout függvények

Az alábbi példa a complete_after és cancel_after_timeout függvényeket mutatja be. A complete_after függvény létrehoz egy task objektumot, amely a megadott késleltetés után fejeződik be. Egy objektumot timer és egy objektumot call használ egy task_completion_event objektum beállításához a megadott késleltetés után. Az task_completion_event osztály használatával definiálhat egy olyan feladatot, amely egy szál vagy egy másik tevékenység után befejeződik, és jelzi, hogy egy érték elérhető. Amikor az esemény be van állítva, a figyelő feladatok befejeződnek, és a folytatások ütemezetten elindításra kerülnek.

Jótanács

Ha további információt szeretne kapni az Aszinkron ügynökök kódtárának részét képező timer és call osztályokról, tekintse meg az Aszinkron üzenetblokkok című témakört.

A cancel_after_timeout függvény a complete_after függvényre épít, hogy megszakítson egy feladatot, ha az nem fejeződik be egy adott időtúllépés előtt. A cancel_after_timeout függvény két feladatot hoz létre. Az első tevékenység a sikerességet jelzi, és a megadott tevékenység befejeződése után fejeződik be. A második tevékenység a hibát jelzi, és a megadott időtúllépés után fejeződik be. A cancel_after_timeout függvény létrehoz egy folytatási feladatot, amely a sikeres vagy sikertelen feladat befejeződésekor fut. Ha a sikertelen feladat először fejeződik be, a folytatás megszakítja a token forrást a teljes feladat megszakításához.

// 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;
    });
}

Példa: A prímszámok számának kiszámítása

Az alábbi példa többször számítja ki az [0, 1000000] tartományban lévő prímszámok számát. A művelet meghiúsul, ha nem fejeződik be egy adott időkorláton belül. A count_primes függvény bemutatja, hogyan kell használni a cancel_after_timeout függvényt. Megszámolja az adott tartományban lévő prímértékek számát, és meghiúsul, ha a feladat nem fejeződik be a megadott időben. A wmain függvény többször hívja meg a count_primes függvényt. Minden alkalommal megfelezi az időkorlátot. A program akkor fejeződik be, ha a művelet nem fejeződik be az aktuális időkorlátban.

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

Ha ezt a technikát használja a tevékenységek késleltetés utáni megszakítására, bármely el nem indított tevékenység nem indul el az összes tevékenység megszakítása után. Fontos azonban, hogy a hosszú ideig futó feladatok gyorsan reagáljanak a megszakításra. A feladattörléssel kapcsolatos további információkért lásd : Lemondás a PPL-ben.

Példa a teljes kódra

A példa teljes kódja a következő:

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

A kód összeállítása

A kód fordításához másolja ki, majd illessze be egy Visual Studio-projektbe, vagy illessze be egy elnevezett task-delay.cpp fájlba, majd futtassa a következő parancsot egy Visual Studio parancssori ablakban.

cl.exe /EHsc task-delay.cpp

Lásd még

Feladat-párhuzamosság
feladatosztály (egyidejűségi futtatókörnyezet)
cancellation_token_source osztály
cancellation_token osztály
task_completion_event osztály
Időzítő osztály
osztály hívása
Aszinkron üzenetblokkok
Lemondás a PPL-ben