次の方法で共有


方法: 遅延後に完了するタスクを作成する

この例では、concurrency::taskconcurrency::cancellation_token_sourceconcurrency::cancellation_tokenconcurrency::task_completion_eventconcurrency::timerと遅延時間の経過後に完了するタスクを作成すると、concurrency::call クラスを使用する方法を示します。 概要、タイムアウト、予定時間のユーザー入力の遅延の処理などのデータにときどきポーリングするループをビルドするには、このメソッドを使用できます。

使用例

complete_after 関数および cancel_after_timeout 関数の例を次に示します。 complete_after 関数は、指定した遅延完了した task オブジェクトを作成します。 これは timer オブジェクトと task_completion_event オブジェクトを設定するには call オブジェクトと遅延指定を使用しています。 task_completion_event クラスを使用して、値を使用すると、スレッドまたは別のタスクが通知を送信し、完了するタスクを定義します。 イベントが設定されると、リスナーは完全に確認し、継続が実行されるように設定されます。

ヒント

非同期エージェント ライブラリの一部である timercall クラスの詳細については 非同期メッセージ ブロックを参照します。

complete_aftercancel_after_timeout 関数のビルドがそのタスクが指定されたタイムアウトの前に完了するタスクを取り消すために機能します。 cancel_after_timeout 関数は 2 個のタスクを作成します。 最初のタスクは、指定されたタスクが完了すると成功を示し、完了します; 2 番目のタスクは失敗を示し、指定されたタイムアウトの後に完了します。 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 関数を複数回実行する。 それぞれについて、その上限を 2 倍にします。 プログラムは現在の時刻の制限に完成しなかったと終了します。

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

タスク全体が完了した遅延が、unstarted タスクを開始した後でタスクを取り消すこともできます。この方法を使用します。 ただし、キャンセルに適時に応答する、長時間実行されるタスクのために重要です。 この例では、count_primes のメソッドの呼び出しキャンセルに応答 concurrency::is_task_cancellation_requestedcancel_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 のプロジェクトに貼り付けるか、タスク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 における取り消し処理