建議使用 Visual Studio 2017

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

 

如需 Visual Studio 2017 的最新文件請參閱 Visual Studio 2017 文件

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

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

System_CAPS_ICON_tip.jpg 提示

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

如果工作未在指定的逾時之前完成,cancel_after_timeout 函式就會在 complete_after 函式上建置以取消該工作。 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_requestedcancel_current_task 函式以回應取消作業。 (或者,您可以呼叫 concurrency:: interruption_point 函式)。 如需工作取消的詳細資訊,請參閱 取消

以下是這個範例的完整程式碼:

// 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 函式
呼叫類別
非同步訊息區
取消

顯示: