Share via


如何:建立在延遲之後才會完成的工作

這個範例顯示如何使用 concurrency::taskconcurrency::cancellation_token_sourceconcurrency::cancellation_tokenconcurrency::task_completion_eventconcurrency::timerconcurrency::call 類別建立延遲之後完成的工作。 您可以使用這個方法建立執行下列動作的迴圈:偶爾輪詢資料,引入逾時,延遲使用者輸入處理長達一段預先定義的時間等等。

範例

下列範例顯示 complete_aftercancel_after_timeout 函式。 complete_after 函式會完成,在指定的延遲之後的 task 物件。 它會使用 timer 物件和 call 物件來設定 task_completion_event 物件,在指定的延遲之後。 使用 task_completion_event 類別,您可以定義完成的工作,在執行緒或另一個工作表示後值。 當事件設定時,接聽程式的工作完成而且其接續排定執行。

提示

如需 timercall 類別的詳細資訊,這是非同步代理程式程式庫的一部分,請參閱 非同步訊息區

如果該工作不在指定的逾時前,在 complete_after 中的 cancel_after_timeout 函式建置。 cancel_after_timeout 函式會建立兩個工作。 所提供的工作完成後,第一個工作表示成功完成;第二個工作表示失敗並放置在指定的逾時後完成。 cancel_after_timeout 函式會執行接續工作,以成功或失敗的工作。 如果失敗的工作必須先完成,則接續取消語彙基元來源取消整體工作。

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

下列範例會計算範圍 [0, 100000] 中質數的計數多次。 如果在特定時間限制,無法完成作業失敗。 count_primes 函式會示範如何使用 cancel_after_timeout 函式。 如果工作不在提供的時間完成,會在指定的範圍和失敗計數數字填滿。 wmain 函式呼叫 count_primes 函式多次。 每次,它達到時間限制。 在作業在目前時間限制之後,程式完成。

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

當您使用這個技巧取消工作,在延遲,任何尚未開始的工作不會啟動之後,在整個工作取消之後。 然而,對所有長時間執行的工作是重要及時回應取消。 在此範例中, count_primes 方法會呼叫 concurrency::is_task_cancellation_requested 函式和 cancel_current_task 回應取消。(或者,您可以呼叫 concurrency::interruption_point 函式)。 如需工作取消的詳細資訊,請參閱PPL 中的取消

這個範例的完整程式碼:

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

編譯程式碼

若要編譯程式碼,請複製程式碼,然後將它貼入 Visual Studio 專案的檔案中,或貼入名為task-delay.cpp的檔案,然後在 Visual Studio 的 [命令提示字元] 視窗中執行下列命令。

cl.exe /EHsc task-delay.cpp

請參閱

參考

task 類別 (並行執行階段)

cancellation_token_source 類別

cancellation_token 類別

task_completion_event 類別

is_task_cancellation_requested 函式

cancel_current_task 函式

interruption_point 函式

timer 類別

call 類別

概念

工作平行處理原則 (並行執行階段)

非同步訊息區

PPL 中的取消