タスクの並列化 (コンカレンシー ランタイム)Task Parallelism (Concurrency Runtime)

同時実行ランタイムでは、タスクは特定のジョブを実行する作業単位であり、通常は他のタスクと並行して実行されます。In the Concurrency Runtime, a task is a unit of work that performs a specific job and typically runs in parallel with other tasks. タスクは、タスクグループに整理された、よりきめ細かなタスクに分解できます。A task can be decomposed into additional, more fine-grained tasks that are organized into a task group.

非同期コードを記述して、非同期操作が完了したときに操作を実行する場合に、タスクを使用します。You use tasks when you write asynchronous code and want some operation to occur after the asynchronous operation completes. たとえば、タスクを使用してファイルから非同期的に読み取り、別のタスク (このドキュメントで後ほど説明する継続タスク) を使用して、データが使用可能になった後に処理することができます。For example, you could use a task to asynchronously read from a file and then use another task—a continuation task, which is explained later in this document—to process the data after it becomes available. 逆に、タスク グループを使用して、並列処理を分解することができます。Conversely, you can use tasks groups to decompose parallel work into smaller pieces. たとえば、残存作業を 2 つのパーティションに分割する再帰的なアルゴリズムがあるとします。For example, suppose you have a recursive algorithm that divides the remaining work into two partitions. タスク グループを使用すると、これらのパーティションを同時に実行して、分割処理の完了を待つことができます。You can use task groups to run these partitions concurrently, and then wait for the divided work to complete.

ヒント

同じルーチンをコレクションのすべての要素に並列で適用する場合は、タスクまたはタスクグループではなく、 concurrency::p arallel_forなどの並列アルゴリズムを使用します。When you want to apply the same routine to every element of a collection in parallel, use a parallel algorithm, such as concurrency::parallel_for, instead of a task or task group. 並列アルゴリズムの詳細については、「並列アルゴリズム」を参照してください。For more information about parallel algorithms, see Parallel Algorithms.

重要なポイントKey Points

  • ラムダ式に変数を渡すときに参照渡しを使用する場合、タスクが終了するまでその変数の有効期間が続くようにする必要があります。When you pass variables to a lambda expression by reference, you must guarantee that the lifetime of that variable persists until the task finishes.

  • 非同期コードを記述するときは、タスク ( concurrency:: taskクラス) を使用します。Use tasks (the concurrency::task class) when you write asynchronous code. タスク クラスは、コンカレンシー ランタイムではなく、Windows ThreadPool をスケジューラとして使用します。The task class uses the Windows ThreadPool as its scheduler, not the Concurrency Runtime.

  • 並列処理をより小さな部分に分解して、それらの小さなピースが完了するまで待機する場合は、タスクグループ ( concurrency:: task_groupクラスまたはconcurrency::p arallel_invokeアルゴリズム) を使用します。Use task groups (the concurrency::task_group class or the concurrency::parallel_invoke algorithm) when you want to decompose parallel work into smaller pieces and then wait for those smaller pieces to complete.

  • 継続を作成するには、 concurrency:: task:: thenメソッドを使用します。Use the concurrency::task::then method to create continuations. 継続は、別のタスクが完了した後に非同期的に実行されるタスクです。A continuation is a task that runs asynchronously after another task completes. 一連の非同期処理を形成するために、継続をいくつでも接続できます。You can connect any number of continuations to form a chain of asynchronous work.

  • タスク ベースの継続は、継続元タスクが取り消されたり、例外をスローした場合でも、継続元タスクが完了すると常に実行がスケジュールされます。A task-based continuation is always scheduled for execution when the antecedent task finishes, even when the antecedent task is canceled or throws an exception.

  • Concurrency:: when_allを使用して、一連のタスクのすべてのメンバーが完了した後に完了するタスクを作成します。Use concurrency::when_all to create a task that completes after every member of a set of tasks completes. タスクのセットの1つのメンバーが完了した後に完了するタスクを作成するには、 concurrency:: when_anyを使用します。Use concurrency::when_any to create a task that completes after one member of a set of tasks completes.

  • タスクとタスク グループは、並列パターン ライブラリ (PPL) の取り消し機構に参加できます。Tasks and task groups can participate in the Parallel Patterns Library (PPL) cancellation mechanism. 詳細については、「 PPL における取り消し処理」を参照してください。For more information, see Cancellation in the PPL.

  • タスクとタスクグループによってスローされた例外をランタイムが処理する方法については、「例外処理」を参照してください。To learn how the runtime handles exceptions that are thrown by tasks and task groups, see Exception Handling.

目次In this Document

ラムダ式の使用Using Lambda Expressions

ラムダ式は簡潔な構文であるため、タスクおよびタスク グループで実行される作業を定義する一般的な方法です。Because of their succinct syntax, lambda expressions are a common way to define the work that is performed by tasks and task groups. 使用のヒントを次に示します。Here are some usage tips:

  • 通常、タスクはバックグラウンド スレッドで実行されるため、ラムダ式の変数をキャプチャする場合にはオブジェクトの有効期間に注意してください。Because tasks typically run on background threads, be aware of the object lifetime when you capture variables in lambda expressions. 変数を値でキャプチャすると、その変数のコピーがラムダの本体に作成されます。When you capture a variable by value, a copy of that variable is made in the lambda body. 参照によってキャプチャする場合には、コピーは作成されません。When you capture by reference, a copy is not made. したがって、参照によってキャプチャするすべての変数の有効期間が、それを使用するタスクのために十分であるように注意します。Therefore, ensure that the lifetime of any variable that you capture by reference outlives the task that uses it.

  • ラムダ式をタスクに渡す場合は、参照渡しでスタックに割り当てられた変数をキャプチャしないでください。When you pass a lambda expression to a task, don't capture variables that are allocated on the stack by reference.

  • ラムダ式でキャプチャする変数を明示的に指定して、値によって、また参照渡しでキャプチャしているものを識別できるようにします。Be explicit about the variables you capture in lambda expressions so that you can identify what you're capturing by value versus by reference. このため、ラムダ式に対して [=] または [&] オプションを使用しないことをお勧めします。For this reason we recommend that you do not use the [=] or [&] options for lambda expressions.

一般的なパターンは、継続のチェーンの 1 つのタスクが変数に割り当てられ、別のタスクがその変数を読み取る場合です。A common pattern is when one task in a continuation chain assigns to a variable, and another task reads that variable. 各継続タスクは変数の異なるコピーを保持するため、値によってキャプチャすることはできません。You can't capture by value because each continuation task would hold a different copy of the variable. スタック割り当て変数の場合は、変数が無効になっている可能性があるため、参照によってキャプチャすることもできません。For stack-allocated variables, you also can't capture by reference because the variable may no longer be valid.

この問題を解決するには、 std:: shared_ptrなどのスマートポインターを使用して変数をラップし、スマートポインターを値で渡します。To solve this problem, use a smart pointer, such as std::shared_ptr, to wrap the variable and pass the smart pointer by value. この方法を使用すると、基になるオブジェクトが割り当てられ、読み込むことができ、それを使用するタスクのために十分な有効期間となります。In this way, the underlying object can be assigned to and read from, and will outlive the tasks that use it. 変数が Windows ランタイム オブジェクトへのポインターであったり、参照カウント ハンドル (^) である場合でも、この方法を使用できます。Use this technique even when the variable is a pointer or a reference-counted handle (^) to a Windows Runtime object. 基本的な例を次に示します。Here's a basic example:

// lambda-task-lifetime.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
#include <string>

using namespace concurrency;
using namespace std;

task<wstring> write_to_string()
{
    // Create a shared pointer to a string that is 
    // assigned to and read by multiple tasks.
    // By using a shared pointer, the string outlives
    // the tasks, which can run in the background after
    // this function exits.
    auto s = make_shared<wstring>(L"Value 1");

    return create_task([s] 
    {
        // Print the current value.
        wcout << L"Current value: " << *s << endl;
        // Assign to a new value.
        *s = L"Value 2";

    }).then([s] 
    {
        // Print the current value.
        wcout << L"Current value: " << *s << endl;
        // Assign to a new value and return the string.
        *s = L"Value 3";
        return *s;
    });
}

int wmain()
{
    // Create a chain of tasks that work with a string.
    auto t = write_to_string();

    // Wait for the tasks to finish and print the result.
    wcout << L"Final value: " << t.get() << endl;
}

/* Output:
    Current value: Value 1
    Current value: Value 2
    Final value: Value 3
*/

ラムダ式について詳しくは、「ラムダ式」をご覧ください。For more information about lambda expressions, see Lambda Expressions.

タスククラスThe task Class

Concurrency:: taskクラスを使用して、一連の依存操作にタスクを作成できます。You can use the concurrency::task class to compose tasks into a set of dependent operations. このコンポジションモデルは、継続の概念によってサポートされています。This composition model is supported by the notion of continuations. 継続を使用すると、前のタスクまたは継続元タスクが完了したときにコードを実行できます。A continuation enables code to be executed when the previous, or antecedent, task completes. 継続元タスクの結果は、1 つ以上の継続タスクへの入力として渡されます。The result of the antecedent task is passed as the input to the one or more continuation tasks. 継続元タスクが完了すると、それを待っているすべての継続タスクが実行のためにスケジュールされます。When an antecedent task completes, any continuation tasks that are waiting on it are scheduled for execution. 各継続タスクは継続元タスクの結果のコピーを受信します。Each continuation task receives a copy of the result of the antecedent task. また、これらの継続タスクが、他の継続の継続元タスクである場合もあり、このようにしてタスクのチェーンが作成されます。In turn, those continuation tasks may also be antecedent tasks for other continuations, thereby creating a chain of tasks. 継続を使うと、特定の依存関係を持つ、任意の長さのタスクのチェーンを作成できます。Continuations help you create arbitrary-length chains of tasks that have specific dependencies among them. また、タスクは開始前または実行中に協調的に、取り消しに参加できます。In addition, a task can participate in cancellation either before a tasks starts or in a cooperative manner while it is running. このキャンセルモデルの詳細については、「 PPL における取り消し処理」を参照してください。For more information about this cancellation model, see Cancellation in the PPL.

task はテンプレート クラスです。task is a template class. 型パラメーター T は、タスクで生成される結果の型です。The type parameter T is the type of the result that is produced by the task. void タスクが値を返さない場合、この型はになる可能性があります。This type can be void if the task does not return a value. T修飾子を使用することはできません constT cannot use the const modifier.

タスクを作成するときに、タスク本体を実行する作業関数を提供します。When you create a task, you provide a work function that performs the task body. この処理関数には、ラムダ関数、関数ポインター、または関数オブジェクトを使用できます。This work function comes in the form of a lambda function, function pointer, or function object. 結果を取得せずにタスクが終了するまで待機するには、 concurrency:: task:: waitメソッドを呼び出します。To wait for a task to finish without obtaining the result, call the concurrency::task::wait method. メソッドは、 task::wait タスクが完了したか取り消されたかを示すconcurrency:: task_status値を返します。The task::wait method returns a concurrency::task_status value that describes whether the task was completed or canceled. タスクの結果を取得するには、 concurrency:: task:: getメソッドを呼び出します。To get the result of the task, call the concurrency::task::get method. このメソッドは、task::wait を呼び出してタスクの完了を待ち、結果が使用できるようになるまで現在のスレッドの実行をブロックします。This method calls task::wait to wait for the task to finish, and therefore blocks execution of the current thread until the result is available.

次の例では、タスクを作成し、結果を待って、値を表示する方法を示します。The following example shows how to create a task, wait for its result, and display its value. このドキュメントの例では、より簡潔な構文を提供するため、ラムダ関数を使用しています。The examples in this documentation use lambda functions because they provide a more succinct syntax. しかし、タスクを使用する場合には、関数ポインター、および関数オブジェクトを使用することも可能です。However, you can also use function pointers and function objects when you use tasks.

// basic-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Create a task.
    task<int> t([]()
    {
        return 42;
    });

    // In this example, you don't necessarily need to call wait() because
    // the call to get() also waits for the result.
    t.wait();

    // Print the result.
    wcout << t.get() << endl;
}

/* Output:
    42
*/

Concurrency:: create_task関数を使用する場合は、型を宣言する代わりに、キーワードを使用でき auto ます。When you use the concurrency::create_task function, you can use the auto keyword instead of declaring the type. たとえば、単位行列を作成して印刷する、次のコードを考えてみます。For example, consider this code that creates and prints the identity matrix:

// create-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <string>
#include <iostream>
#include <array>

using namespace concurrency;
using namespace std;

int wmain()
{
    task<array<array<int, 10>, 10>> create_identity_matrix([]
    {
        array<array<int, 10>, 10> matrix;
        int row = 0;
        for_each(begin(matrix), end(matrix), [&row](array<int, 10>& matrixRow) 
        {
            fill(begin(matrixRow), end(matrixRow), 0);
            matrixRow[row] = 1;
            row++;
        });
        return matrix;
    });

    auto print_matrix = create_identity_matrix.then([](array<array<int, 10>, 10> matrix)
    {
        for_each(begin(matrix), end(matrix), [](array<int, 10>& matrixRow) 
        {
            wstring comma;
            for_each(begin(matrixRow), end(matrixRow), [&comma](int n) 
            {
                wcout << comma << n;
                comma = L", ";
            });
            wcout << endl;
        });
    });

    print_matrix.wait();
}
/* Output:
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0
    0, 1, 0, 0, 0, 0, 0, 0, 0, 0
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0
    0, 0, 0, 1, 0, 0, 0, 0, 0, 0
    0, 0, 0, 0, 1, 0, 0, 0, 0, 0
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0
    0, 0, 0, 0, 0, 0, 1, 0, 0, 0
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0
    0, 0, 0, 0, 0, 0, 0, 0, 1, 0
    0, 0, 0, 0, 0, 0, 0, 0, 0, 1
*/

create_task 関数を使用して同等の操作を行うことができます。You can use the create_task function to create the equivalent operation.

auto create_identity_matrix = create_task([]
{
    array<array<int, 10>, 10> matrix;
    int row = 0;
    for_each(begin(matrix), end(matrix), [&row](array<int, 10>& matrixRow) 
    {
        fill(begin(matrixRow), end(matrixRow), 0);
        matrixRow[row] = 1;
        row++;
    });
    return matrix;
});

タスクの実行時に例外がスローされた場合、ランタイムは、それ以降の task::get または task::wait の呼び出し、またはタスク ベースの継続への呼び出しで、その例外をマーシャリングします。If an exception is thrown during the execution of a task, the runtime marshals that exception in the subsequent call to task::get or task::wait, or to a task-based continuation. タスク例外処理機構の詳細については、「例外処理」を参照してください。For more information about the task exception-handling mechanism, see Exception Handling.

taskConcurrency:: task_completion_event、キャンセルの使用例については、「チュートリアル: タスクと XML HTTP 要求を使用した接続」を参照してください。For an example that uses task, concurrency::task_completion_event, cancellation, see Walkthrough: Connecting Using Tasks and XML HTTP Requests. (task_completion_event クラスについてはドキュメントの後の部分で説明されています。)(The task_completion_event class is described later in this document.)

ヒント

UWP アプリのタスクに固有の詳細については、「 c++ での非同期プログラミング」および「 C++ での uwp アプリ用の非同期操作の作成」を参照してください。To learn details that are specific to tasks in UWP apps, see Asynchronous programming in C++ and Creating Asynchronous Operations in C++ for UWP Apps.

継続タスクContinuation Tasks

非同期プログラミングでは、非同期操作で完了時に 2 番目の操作を呼び出してデータを渡すのが一般的です。In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. これまで、この処理はコールバック メソッドを使用して行っていました。Traditionally, this is done by using callback methods. 同時実行ランタイムでは、継続タスクによって同じ機能が提供されます。In the Concurrency Runtime, the same functionality is provided by continuation tasks. 継続タスク ("継続" とも呼ばれます) は、継続元が完了したときに、継続元と呼ばれる別のタスクによって呼び出される非同期タスクです。A continuation task (also known just as a continuation) is an asynchronous task that is invoked by another task, which is known as the antecedent, when the antecedent completes. 継続を使用して、次の操作を行うことができます。By using continuations, you can:

  • 継続元のデータを継続に渡します。Pass data from the antecedent to the continuation.

  • 継続を呼び出す場合、呼び出さない場合についての正確な条件を指定します。Specify the precise conditions under which the continuation is invoked or not invoked.

  • 継続が開始される前、または継続の実行中に、継続を取り消します。Cancel a continuation either before it starts or cooperatively while it is running.

  • 継続をスケジュールする方法についてのヒントを提供します。Provide hints about how the continuation should be scheduled. (これはユニバーサル Windows プラットフォーム (UWP) アプリにのみ適用されます。(This applies to Universal Windows Platform (UWP) apps only. 詳細については、「 C++ での UWP アプリ用の非同期操作の作成」を参照してください。)For more information, see Creating Asynchronous Operations in C++ for UWP Apps.)

  • 同じ継続元から複数の継続を呼び出します。Invoke multiple continuations from the same antecedent.

  • 複数の継続元のすべてまたはいずれかが完了したときに 1 つの継続を呼び出します。Invoke one continuation when all or any of multiple antecedents complete.

  • 任意の長さで連続して継続を実行します。Chain continuations one after another to any length.

  • 継続元によってスローされた例外を処理するために継続を使用します。Use a continuation to handle exceptions that are thrown by the antecedent.

これらの機能を使うと、最初のタスクが完了したときに、1 つ以上のタスクを実行できます。These features enable you to execute one or more tasks when the first task completes. たとえば、最初のタスクがディスクからデータを読み取った後に、ファイルを圧縮する継続を作成できます。For example, you can create a continuation that compresses a file after the first task reads it from disk.

次の例では、前の例を変更して、 concurrency:: task:: thenメソッドを使用して、継続元タスクが使用可能になったときの値を出力する継続をスケジュールします。The following example modifies the previous one to use the concurrency::task::then method to schedule a continuation that prints the value of the antecedent task when it is available.

// basic-continuation.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]() -> int
    {
        return 42;
    });

    t.then([](int result)
    {
        wcout << result << endl;
    }).wait();

    // Alternatively, you can chain the tasks directly and
    // eliminate the local variable.
    /*create_task([]() -> int
    {
        return 42;
    }).then([](int result)
    {
        wcout << result << endl;
    }).wait();*/
}

/* Output:
    42
*/

タスクを任意の長さにチェーンしたり、入れ子にできます。You can chain and nest tasks to any length. またタスクは、複数の継続を持つことができます。A task can also have multiple continuations. 次の例では、前のタスクの値を 3 回インクリメントする、基本的な継続のチェーンを示しています。The following example illustrates a basic continuation chain that increments the value of the previous task three times.

// continuation-chain.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]() -> int
    { 
        return 0;
    });
    
    // Create a lambda that increments its input value.
    auto increment = [](int n) { return n + 1; };

    // Run a chain of continuations and print the result.
    int result = t.then(increment).then(increment).then(increment).get();
    wcout << result << endl;
}

/* Output:
    3
*/

継続は、別のタスクを返すこともできます。A continuation can also return another task. 取り消されない場合、このタスクは後続の継続の前に実行されます。If there is no cancellation, then this task is executed before the subsequent continuation. この手法は、非同期ラップ解除と呼ばれます。This technique is known as asynchronous unwrapping. 非同期ラッピング解除は、追加の作業をバックグラウンドで実行するときに、現在のタスクが現在のスレッドをブロックしないようにする場合に役立ちます。Asynchronous unwrapping is useful when you want to perform additional work in the background, but do not want the current task to block the current thread. (これは UWP アプリで共通であり、継続は UI スレッドで実行できます)。(This is common in UWP apps, where continuations can run on the UI thread). 次の例は、3 つのタスクを示しています。The following example shows three tasks. 最初のタスクは、継続タスクの前に実行される、別のタスクを返します。The first task returns another task that is run before a continuation task.

// async-unwrapping.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t = create_task([]()
    {
        wcout << L"Task A" << endl;

        // Create an inner task that runs before any continuation
        // of the outer task.
        return create_task([]()
        {
            wcout << L"Task B" << endl;
        });
    });
  
    // Run and wait for a continuation of the outer task.
    t.then([]()
    {
        wcout << L"Task C" << endl;
    }).wait();
}

/* Output:
    Task A
    Task B
    Task C
*/

重要

タスクの継続が N 型の入れ子のタスクを返す場合、結果のタスクは N 型でなく task<N> 型となり、入れ子のタスクが完了すると終了します。When a continuation of a task returns a nested task of type N, the resulting task has the type N, not task<N>, and completes when the nested task completes. つまり、継続は入れ子のタスクのラッピング解除を行います。In other words, the continuation performs the unwrapping of the nested task.

値ベースの継続とタスクベースの継続Value-Based Versus Task-Based Continuations

task オブジェクトは戻り値の型が T であるため、その継続タスクに T または task<T> 型の値を指定できます。Given a task object whose return type is T, you can provide a value of type T or task<T> to its continuation tasks. 型を受け取る継続 T は、値ベースの継続と呼ばれます。A continuation that takes type T is known as a value-based continuation. 値ベースの継続は、継続元タスクがエラーなしに完了して取り消されない場合に、実行のためにスケジュールされます。A value-based continuation is scheduled for execution when the antecedent task completes without error and is not canceled. パラメーターとして型を受け取る継続は、 task<T> タスクベースの継続と呼ばれます。A continuation that takes type task<T> as its parameter is known as a task-based continuation. タスク ベースの継続は、継続元タスクが取り消されたり、例外をスローした場合でも、継続元タスクが完了すると常に実行がスケジュールされます。A task-based continuation is always scheduled for execution when the antecedent task finishes, even when the antecedent task is canceled or throws an exception. 次に task::get を呼び出して、継続元タスクの結果を取得できます。You can then call task::get to get the result of the antecedent task. 継続元タスクがキャンセルされた場合は、 task::get concurrency:: task_canceledをスローします。If the antecedent task was canceled, task::get throws concurrency::task_canceled. 継続元タスクが例外をスローすると、task::get は再度、例外をスローします。If the antecedent task threw an exception, task::get rethrows that exception. タスク ベースの継続は、その継続元タスクが取り消された場合、取り消し済みとしてマークされません。A task-based continuation is not marked as canceled when its antecedent task is canceled.

タスクの作成Composing Tasks

ここでは、複数のタスクを構成して一般的なパターンを実装するのに役立つconcurrency:: when_allおよびconcurrency:: when_any関数について説明します。This section describes the concurrency::when_all and concurrency::when_any functions, which can help you compose multiple tasks to implement common patterns.

When_all 関数The when_all Function

when_all 関数は、一連のタスクの完了後に完了するタスクを生成します。The when_all function produces a task that completes after a set of tasks complete. この関数は、セット内の各タスクの結果を含む std::vectorオブジェクトを返します。This function returns a std::vector object that contains the result of each task in the set. 次の基本的な例では、when_all を使用して、3 つの他のタスクの完了を表すタスクを作成します。The following basic example uses when_all to create a task that represents the completion of three other tasks.

// join-tasks.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Start multiple tasks.
    array<task<void>, 3> tasks = 
    {
        create_task([] { wcout << L"Hello from taskA." << endl; }),
        create_task([] { wcout << L"Hello from taskB." << endl; }),
        create_task([] { wcout << L"Hello from taskC." << endl; })
    };

    auto joinTask = when_all(begin(tasks), end(tasks));

    // Print a message from the joining thread.
    wcout << L"Hello from the joining thread." << endl;

    // Wait for the tasks to finish.
    joinTask.wait();
}

/* Sample output:
    Hello from the joining thread.
    Hello from taskA.
    Hello from taskC.
    Hello from taskB.
*/

注意

when_all に渡すタスクは均一である必要があります。The tasks that you pass to when_all must be uniform. つまり、これらはすべて同じ型を返す必要があります。In other words, they must all return the same type.

次に示す例のように、&& 構文を使用して、一連のタスクの完了後に完了するタスクを生成することもできます。You can also use the && syntax to produce a task that completes after a set of tasks complete, as shown in the following example.

auto t = t1 && t2; // same as when_all

通常は、継続を when_all と共に使用して、一連のタスクが終了した後に操作を実行します。It is common to use a continuation together with when_all to perform an action after a set of tasks finishes. 次の例では、前の例を変更して、それぞれが結果を生成する3つのタスクの合計を出力し int ます。The following example modifies the previous one to print the sum of three tasks that each produce an int result.

// Start multiple tasks.
array<task<int>, 3> tasks =
{
    create_task([]() -> int { return 88; }),
    create_task([]() -> int { return 42; }),
    create_task([]() -> int { return 99; })
};

auto joinTask = when_all(begin(tasks), end(tasks)).then([](vector<int> results)
{
    wcout << L"The sum is " 
          << accumulate(begin(results), end(results), 0)
          << L'.' << endl;
});

// Print a message from the joining thread.
wcout << L"Hello from the joining thread." << endl;

// Wait for the tasks to finish.
joinTask.wait();

/* Output:
    Hello from the joining thread.
    The sum is 229.
*/

この例では、タスク ベースの継続を作成するために task<vector<int>> を指定することもできます。In this example, you can also specify task<vector<int>> to produce a task-based continuation.

一連のタスクのいずれかのタスクが取り消されたり、例外をスローした場合、when_all は残りのタスクを完了を待たずに、直ちに終了します。If any task in a set of tasks is canceled or throws an exception, when_all immediately completes and does not wait for the remaining tasks to finish. 例外がスローされた場合、task::get を返すタスク オブジェクトで task::wait または when_all を呼び出すと、ランタイムは再度、例外をスローします。If an exception is thrown, the runtime rethrows the exception when you call task::get or task::wait on the task object that when_all returns. 複数のタスクからスローすると、ランタイムはその中の 1 つを選択します。If more than one task throws, the runtime chooses one of them. したがって、すべてのタスクが完了してからすべての例外を確認するようにします。タスクの例外がハンドルされない場合、アプリケーションは終了します。Therefore, ensure that you observe all exceptions after all tasks complete; an unhandled task exception causes the app to terminate.

次のユーティリティ関数を使用すると、プログラムがすべての例外を監視できるようになります。Here's a utility function that you can use to ensure that your program observes all exceptions. 指定された範囲のタスクごとに、observe_all_exceptions は再スローされる例外をトリガーし、その例外を受け取ります。For each task in the provided range, observe_all_exceptions triggers any exception that occurred to be rethrown and then swallows that exception.

// Observes all exceptions that occurred in all tasks in the given range.
template<class T, class InIt> 
void observe_all_exceptions(InIt first, InIt last) 
{
    std::for_each(first, last, [](concurrency::task<T> t)
    {
        t.then([](concurrency::task<T> previousTask)
        {
            try
            {
                previousTask.get();
            }
            // Although you could catch (...), this demonstrates how to catch specific exceptions. Your app
            // might handle different exception types in different ways.
            catch (Platform::Exception^)
            {
                // Swallow the exception.
            }
            catch (const std::exception&)
            {
                // Swallow the exception.
            }
        });
    });
}

C++ および XAML を使用し、一連のファイルをディスクに書き込む UWP アプリについて考えてみましょう。Consider a UWP app that uses C++ and XAML and writes a set of files to disk. 次の例は、when_allobserve_all_exceptions を使用して、プログラムがすべての例外を確認する方法を示します。The following example shows how to use when_all and observe_all_exceptions to ensure that the program observes all exceptions.

// Writes content to files in the provided storage folder.
// The first element in each pair is the file name. The second element holds the file contents.
task<void> MainPage::WriteFilesAsync(StorageFolder^ folder, const vector<pair<String^, String^>>& fileContents)
{
    // For each file, create a task chain that creates the file and then writes content to it. Then add the task chain to a vector of tasks.
    vector<task<void>> tasks;
    for (auto fileContent : fileContents)
    {
        auto fileName = fileContent.first;
        auto content = fileContent.second;

        // Create the file. The CreationCollisionOption::FailIfExists flag specifies to fail if the file already exists.
        tasks.emplace_back(create_task(folder->CreateFileAsync(fileName, CreationCollisionOption::FailIfExists)).then([content](StorageFile^ file)
        {
            // Write its contents.
            return create_task(FileIO::WriteTextAsync(file, content));
        }));
    }

    // When all tasks finish, create a continuation task that observes any exceptions that occurred.
    return when_all(begin(tasks), end(tasks)).then([tasks](task<void> previousTask)
    {
        task_status status = completed;
        try
        {
            status = previousTask.wait();
        }
        catch (COMException^ e)
        {
            // We'll handle the specific errors below.
        }
        // TODO: If other exception types might happen, add catch handlers here.

        // Ensure that we observe all exceptions.
        observe_all_exceptions<void>(begin(tasks), end(tasks));

        // Cancel any continuations that occur after this task if any previous task was canceled.
        // Although cancellation is not part of this example, we recommend this pattern for cases that do.
        if (status == canceled)
        {
            cancel_current_task();
        }
    });
}
この例を実行するにはTo run this example
  1. MainPage.xaml で、Button コントロールを追加します。In MainPage.xaml, add a Button control.
<Button x:Name="Button1" Click="Button_Click">Write files</Button>
  1. Mainpage.xaml で、これらの事前宣言を private クラス宣言のセクションに追加します MainPageIn MainPage.xaml.h, add these forward declarations to the private section of the MainPage class declaration.
void Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
concurrency::task<void> WriteFilesAsync(Windows::Storage::StorageFolder^ folder, const std::vector<std::pair<Platform::String^, Platform::String^>>& fileContents);
  1. MainPage.xaml.cpp で、Button_Click イベント ハンドラーを実装します。In MainPage.xaml.cpp, implement the Button_Click event handler.
// A button click handler that demonstrates the scenario.
void MainPage::Button_Click(Object^ sender, RoutedEventArgs^ e)
{
    // In this example, the same file name is specified two times. WriteFilesAsync fails if one of the files already exists.
    vector<pair<String^, String^>> fileContents;
    fileContents.emplace_back(make_pair(ref new String(L"file1.txt"), ref new String(L"Contents of file 1")));
    fileContents.emplace_back(make_pair(ref new String(L"file2.txt"), ref new String(L"Contents of file 2")));
    fileContents.emplace_back(make_pair(ref new String(L"file1.txt"), ref new String(L"Contents of file 3")));

    Button1->IsEnabled = false; // Disable the button during the operation.
    WriteFilesAsync(ApplicationData::Current->TemporaryFolder, fileContents).then([this](task<void> previousTask)
    {
        try
        {
            previousTask.get();
        }
        // Although cancellation is not part of this example, we recommend this pattern for cases that do.
        catch (const task_canceled&)
        {
            // Your app might show a message to the user, or handle the error in some other way.
        }

        Button1->IsEnabled = true; // Enable the button.
    });
}
  1. MainPage.xaml.cpp で、例に示すように WriteFilesAsync を実装します。In MainPage.xaml.cpp, implement WriteFilesAsync as shown in the example.

ヒント

when_all は、その結果、task を生成する、非ブロッキング関数です。when_all is a non-blocking function that produces a task as its result. Task:: waitとは異なり、ASTA (アプリケーション STA) スレッドの UWP アプリでこの関数を呼び出すことは安全です。Unlike task::wait, it is safe to call this function in a UWP app on the ASTA (Application STA) thread.

When_any 関数The when_any Function

when_any 関数は、一連のタスクの最初のタスクの完了後に完了するタスクを生成します。The when_any function produces a task that completes when the first task in a set of tasks completes. この関数は、完了したタスクの結果と set 内のそのタスクのインデックスを含むstd::p airオブジェクトを返します。This function returns a std::pair object that contains the result of the completed task and the index of that task in the set.

when_any 関数は、特に次のシナリオに役立ちます。The when_any function is especially useful in the following scenarios:

  • 重複した操作。Redundant operations. 多くの方法で実行できるアルゴリズムまたは操作を検討してください。Consider an algorithm or operation that can be performed in many ways. when_any 関数を使用すると、最初の操作を完了して残りの操作を取り消すように、操作を選択できます。You can use the when_any function to select the operation that finishes first and then cancel the remaining operations.

  • インタリーブされた操作。Interleaved operations. 複数の操作を開始して、それらの操作のすべてが完了し、各操作が完了したら when_any 関数を使って結果を処理するようにできます。You can start multiple operations that all must finish and use the when_any function to process results as each operation finishes. 1 つの操作が完了したら、1 つ以上の追加タスクを開始できます。After one operation finishes, you can start one or more additional tasks.

  • 制限された操作。Throttled operations. when_any 関数を使用して、前のシナリオを拡張し、同時操作の数を制限することができます。You can use the when_any function to extend the previous scenario by limiting the number of concurrent operations.

  • 有効期限切れの操作。Expired operations. when_any 関数を使用して、1 つ以上のタスクと特定の時間以降に完了する 1 つのタスクから選択することができます。You can use the when_any function to select between one or more tasks and a task that finishes after a specific time.

when_all と同様に、通常は継続を when_any と共に使用して、一連タスクの最初のタスクが終了した後に操作を実行します。As with when_all, it is common to use a continuation that has when_any to perform action when the first in a set of tasks finish. 次の基本的な例では、when_any を使用して、他の 3 つのタスクの最初のタスクが完了したときに完了するタスクを作成します。The following basic example uses when_any to create a task that completes when the first of three other tasks completes.

// select-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Start multiple tasks.
    array<task<int>, 3> tasks = {
        create_task([]() -> int { return 88; }),
        create_task([]() -> int { return 42; }),
        create_task([]() -> int { return 99; })
    };

    // Select the first to finish.
    when_any(begin(tasks), end(tasks)).then([](pair<int, size_t> result)
    {
        wcout << "First task to finish returns "
              << result.first
              << L" and has index "
              << result.second
              << L'.' << endl;
    }).wait();
}

/* Sample output:
    First task to finish returns 42 and has index 1.
*/

この例では、タスク ベースの継続を作成するために task<pair<int, size_t>> を指定することもできます。In this example, you can also specify task<pair<int, size_t>> to produce a task-based continuation.

注意

when_all と同様に、when_any に渡すタスクはすべて同じ型を返す必要があります。As with when_all, the tasks that you pass to when_any must all return the same type.

次に示す例のように、|| 構文を使用して、一連のタスクの最初のタスクの完了後に完了するタスクを生成することもできます。You can also use the || syntax to produce a task that completes after the first task in a set of tasks completes, as shown in the following example.

auto t = t1 || t2; // same as when_any

ヒント

と同様に when_allwhen_any は非ブロッキングであり、ASTA スレッドの UWP アプリで安全に呼び出すことができます。As with when_all, when_any is non-blocking and is safe to call in a UWP app on the ASTA thread.

遅延タスクの実行Delayed Task Execution

条件が満たされるまで、または外部イベントに応答してタスクを開始するまで、タスクの実行を遅延する必要がある場合があります。It is sometimes necessary to delay the execution of a task until a condition is satisfied, or to start a task in response to an external event. たとえば、非同期プログラミングでは、I/O 完了のイベントに応答してタスクを開始する必要があります。For example, in asynchronous programming, you might have to start a task in response to an I/O completion event.

これを実現する2つの方法は、継続を使用するか、タスクを開始して、タスクの処理関数内のイベントを待機することです。Two ways to accomplish this are to use a continuation or to start a task and wait on an event inside the task's work function. しかし、これらの方法の 1 つを使用できない場合もあります。However, there are cases where is it not possible to use one of these techniques. たとえば、継続を作成するためには、継続元タスクが必要です。For example, to create a continuation, you must have the antecedent task. ただし、継続元タスクがない場合は、タスクの完了イベントを作成し、後でその完了イベントを継続元のタスクにチェーンすることができます。However, if you do not have the antecedent task, you can create a task completion event and later chain that completion event to the antecedent task when it becomes available. また、待機中のタスクはスレッドをブロックするため、タスクの完了イベントを使用して、非同期操作が完了したときに処理を行い、スレッドを解放することもできます。In addition, because a waiting task also blocks a thread, you can use task completion events to perform work when an asynchronous operation completes, and thereby free a thread.

Concurrency:: task_completion_eventクラスは、このようなタスクの構成を簡略化するのに役立ちます。The concurrency::task_completion_event class helps simplify such composition of tasks. task クラスと同様に、型パラメーター T は、タスクで生成される結果の型です。Like the task class, the type parameter T is the type of the result that is produced by the task. void タスクが値を返さない場合、この型はになる可能性があります。This type can be void if the task does not return a value. T修飾子を使用することはできません constT cannot use the const modifier. 通常、task_completion_event オブジェクトは、値が使用できるようになると通知する、スレッドまたはタスクに提供されます。Typically, a task_completion_event object is provided to a thread or task that will signal it when the value for it becomes available. 同時に、1 つ以上のタスクは、そのイベントのリスナーとして設定されます。At the same time, one or more tasks are set as listeners of that event. イベントが設定されると、リスナー タスクが完了し、継続が実行されるようにスケジュールされます。When the event is set, the listener tasks complete and their continuations are scheduled to run.

を使用して task_completion_event 遅延後に完了するタスクを実装する例については、「方法: 遅延後に完了するタスクを作成する」を参照してください。For an example that uses task_completion_event to implement a task that completes after a delay, see How to: Create a Task that Completes After a Delay.

タスクグループTask Groups

タスクグループは、タスクのコレクションを整理します。A task group organizes a collection of tasks. タスク グループでは、ワーク スティーリング キューにタスクを置きます。Task groups push tasks on to a work-stealing queue. スケジューラはこのキューからタスクを削除し、使用できるコンピューティング リソースでそのタスクを実行します。The scheduler removes tasks from this queue and executes them on available computing resources. タスク グループにタスクを追加した場合、すべてのタスクが終了するまで待機することも、まだ開始されていないタスクを取り消すこともできます。After you add tasks to a task group, you can wait for all tasks to finish or cancel tasks that have not yet started.

PPL は、 concurrency:: task_groupおよびconcurrency:: structured_task_groupクラスを使用してタスクグループを表し、 concurrency:: task_handleクラスを使用してこれらのグループで実行されるタスクを表します。The PPL uses the concurrency::task_group and concurrency::structured_task_group classes to represent task groups, and the concurrency::task_handle class to represent the tasks that run in these groups. task_handle クラスは、処理を行うコードをカプセル化します。The task_handle class encapsulates the code that performs work. task クラスと同様に、この処理関数には、ラムダ関数、関数ポインター、または関数オブジェクトを使用できます。Like the task class, the work function comes in the form of a lambda function, function pointer, or function object. 通常、task_handle オブジェクトを直接操作する必要はありません。You typically do not need to work with task_handle objects directly. 代わりに、タスク グループに処理関数を渡します。タスク グループによって task_handle オブジェクトが作成および管理されます。Instead, you pass work functions to a task group, and the task group creates and manages the task_handle objects.

PPL では、タスクグループが非構造化タスクグループと構造化タスクグループの2つのカテゴリに分割されます。The PPL divides task groups into these two categories: unstructured task groups and structured task groups. PPL では、task_group クラスを使用して非構造化タスク グループを表し、structured_task_group クラスを使用して構造化タスク グループを表します。The PPL uses the task_group class to represent unstructured task groups and the structured_task_group class to represent structured task groups.

重要

PPL では、同時実行::p arallel_invokeアルゴリズムも定義されています。このアルゴリズムでは、クラスを使用して structured_task_group 一連のタスクを並列に実行します。The PPL also defines the concurrency::parallel_invoke algorithm, which uses the structured_task_group class to execute a set of tasks in parallel. parallel_invoke アルゴリズムにはより簡潔な構文が用意されているため、可能であれば structured_task_group クラスの代わりに使用することをお勧めします。Because the parallel_invoke algorithm has a more succinct syntax, we recommend that you use it instead of the structured_task_group class when you can. 詳細については、「並列アルゴリズム」を参照して parallel_invoke ください。The topic Parallel Algorithms describes parallel_invoke in greater detail.

parallel_invoke は、同時に実行する独立したタスクが複数あり、すべてのタスクが終了するまで待機してから処理を続行する必要がある場合に使用します。Use parallel_invoke when you have several independent tasks that you want to execute at the same time, and you must wait for all tasks to finish before you continue. この手法は、フォークと結合の並列処理と呼ばれることがよくあります。This technique is often referred to as fork and join parallelism. task_group は、同時に実行する独立したタスクが複数あり、それらのタスクが終了するタイミングがまだ先である場合に使用します。Use task_group when you have several independent tasks that you want to execute at the same time, but you want to wait for the tasks to finish at a later time. たとえば、task_group オブジェクトにタスクを追加して、それらのタスクが別の関数や別のストレッドで終了するまで待機できます。For example, you can add tasks to a task_group object and wait for the tasks to finish in another function or from another thread.

タスク グループでは、キャンセル処理の概念がサポートされています。Task groups support the concept of cancellation. キャンセル処理を使用すると、操作全体を取り消すことをアクティブなすべてのタスクに通知できます。Cancellation enables you to signal to all active tasks that you want to cancel the overall operation. また、キャンセル処理により、まだ開始されていないタスクが実行されるのを防止できます。Cancellation also prevents tasks that have not yet started from starting. 取り消しの詳細については、「 PPL における取り消し処理」を参照してください。For more information about cancellation, see Cancellation in the PPL.

また、ランタイムでは、例外処理モデルを使用することによって、タスクから例外をスローし、関連するタスク グループが終了するまで待機しているときにその例外を処理できます。The runtime also provides an exception-handling model that enables you to throw an exception from a task and handle that exception when you wait for the associated task group to finish. この例外処理モデルの詳細については、「例外処理」を参照してください。For more information about this exception-handling model, see Exception Handling.

Task_group と structured_task_group の比較Comparing task_group to structured_task_group

task_group クラスの代わりに parallel_invoke または structured_task_group を使用することが推奨されますが、可変個のタスクを実行する並列アルゴリズムやキャンセル処理のサポートが必要な並列アルゴリズムを記述する場合など、structured_task_group を使用した方がよい場合もあります。Although we recommend that you use task_group or parallel_invoke instead of the structured_task_group class, there are cases where you want to use structured_task_group, for example, when you write a parallel algorithm that performs a variable number of tasks or requires support for cancellation. ここでは、task_group クラスと structured_task_group クラスの違いについて説明します。This section explains the differences between the task_group and structured_task_group classes.

task_group クラスはスレッド セーフです。The task_group class is thread-safe. したがって、複数のスレッドから task_group オブジェクトにタスクを追加したり、複数のスレッドから task_group オブジェクトに対して待機や取り消しの操作を行ったりしてもかまいません。Therefore you can add tasks to a task_group object from multiple threads and wait on or cancel a task_group object from multiple threads. structured_task_group オブジェクトの構築と破棄は、同じ構文のスコープで行う必要があります。The construction and destruction of a structured_task_group object must occur in the same lexical scope. また、structured_task_group オブジェクトに対する操作はすべて同じスレッドで行う必要があります。In addition, all operations on a structured_task_group object must occur on the same thread. この規則の例外は、 concurrency:: structured_task_group:: cancelメソッドとconcurrency:: structured_task_group:: is_cancelingメソッドです。The exception to this rule is the concurrency::structured_task_group::cancel and concurrency::structured_task_group::is_canceling methods. 子タスクでこれらのメソッドを呼び出すことで、任意のタイミングで親タスク グループを取り消したり、取り消し処理をチェックしたりできます。A child task can call these methods to cancel the parent task group or check for cancelation at any time.

task_group Concurrency:: task_group:: waitメソッドまたはconcurrency:: task_group:: run_and_waitメソッドを呼び出した後、オブジェクトに対して追加のタスクを実行できます。You can run additional tasks on a task_group object after you call the concurrency::task_group::wait or concurrency::task_group::run_and_wait method. 逆に、 structured_task_group concurrency:: structured_task_group:: waitメソッドまたはconcurrency:: structured_task_group:: run_and_waitメソッドを呼び出した後でオブジェクトに対して追加のタスクを実行すると、その動作は未定義になります。Conversely, if you run additional tasks on a structured_task_group object after you call the concurrency::structured_task_group::wait or concurrency::structured_task_group::run_and_wait methods, then the behavior is undefined.

structured_task_group クラスはスレッド間で同期されないため、実行に伴うオーバーヘッドが task_group クラスよりも少なくなります。Because the structured_task_group class does not synchronize across threads, it has less execution overhead than the task_group class. したがって、複数のスレッドから処理をスケジュールする必要のない問題に対処する場合に、parallel_invoke アルゴリズムを使用できないときは、structured_task_group クラスを使用すると、よりパフォーマンスの高いコードを作成できます。Therefore, if your problem does not require that you schedule work from multiple threads and you cannot use the parallel_invoke algorithm, the structured_task_group class can help you write better performing code.

structured_task_group オブジェクトを別の structured_task_group オブジェクト内で使用する場合は、外部オブジェクトが終了する前に内部オブジェクトが終了して破棄される必要があります。If you use one structured_task_group object inside another structured_task_group object, the inner object must finish and be destroyed before the outer object finishes. task_group クラスの場合、外部グループが終了する前に、入れ子になったタスク グループが終了する必要はありません。The task_group class does not require for nested task groups to finish before the outer group finishes.

非構造化タスク グループと構造化タスク グループでは、さまざまな方法でタスク ハンドルを操作します。Unstructured task groups and structured task groups work with task handles in different ways. task_group オブジェクトには処理関数を直接渡すことができます。task_group オブジェクトによってタスク ハンドルが自動的に作成および管理されます。You can pass work functions directly to a task_group object; the task_group object will create and manage the task handle for you. structured_task_group クラスを使用する場合は、タスクごとに task_handle オブジェクトを管理する必要があります。The structured_task_group class requires you to manage a task_handle object for each task. task_handle オブジェクトは、関連する structured_task_group オブジェクトの有効期間を通じて有効である必要があります。Every task_handle object must remain valid throughout the lifetime of its associated structured_task_group object. 次の基本的な例に示すように、 concurrency:: make_task関数を使用してオブジェクトを作成し task_handle ます。Use the concurrency::make_task function to create a task_handle object, as shown in the following basic example:

// make-task-structure.cpp
// compile with: /EHsc
#include <ppl.h>

using namespace concurrency;

int wmain()
{
   // Use the make_task function to define several tasks.
   auto task1 = make_task([] { /*TODO: Define the task body.*/ });
   auto task2 = make_task([] { /*TODO: Define the task body.*/ });
   auto task3 = make_task([] { /*TODO: Define the task body.*/ });

   // Create a structured task group and run the tasks concurrently.

   structured_task_group tasks;

   tasks.run(task1);
   tasks.run(task2);
   tasks.run_and_wait(task3);
}

可変個のタスクがある場合にタスクハンドルを管理するには、 _mallocaなどのスタック割り当てルーチン、または std::vectorなどのコンテナークラスを使用します。To manage task handles for cases where you have a variable number of tasks, use a stack-allocation routine such as _malloca or a container class, such as std::vector.

task_groupstructured_task_group の両方でキャンセル処理がサポートされています。Both task_group and structured_task_group support cancellation. 取り消しの詳細については、「 PPL における取り消し処理」を参照してください。For more information about cancellation, see Cancellation in the PPL.

Example

次の簡単な例では、タスク グループの操作方法を示します。The following basic example shows how to work with task groups. この例では、parallel_invoke アルゴリズムを使用して、2 つのタスクを同時に実行します。This example uses the parallel_invoke algorithm to perform two tasks concurrently. 各タスクでは、サブタスクを task_group オブジェクトに追加します。Each task adds sub-tasks to a task_group object. task_group クラスを使用した場合、複数のタスクでタスクを同時に追加できることに注意してください。Note that the task_group class allows for multiple tasks to add tasks to it concurrently.

// using-task-groups.cpp
// compile with: /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>

using namespace concurrency;
using namespace std;

// Prints a message to the console.
template<typename T>
void print_message(T t)
{
   wstringstream ss;
   ss << L"Message from task: " << t << endl;
   wcout << ss.str(); 
}

int wmain()
{  
   // A task_group object that can be used from multiple threads.
   task_group tasks;

   // Concurrently add several tasks to the task_group object.
   parallel_invoke(
      [&] {
         // Add a few tasks to the task_group object.
         tasks.run([] { print_message(L"Hello"); });
         tasks.run([] { print_message(42); });
      },
      [&] {
         // Add one additional task to the task_group object.
         tasks.run([] { print_message(3.14); });
      }
   );

   // Wait for all tasks to finish.
   tasks.wait();
}

この例のサンプル出力を次に示します。The following is sample output for this example:

Message from task: Hello
Message from task: 3.14
Message from task: 42

parallel_invoke アルゴリズムではタスクを同時に実行するため、出力メッセージの順序が変わる可能性があります。Because the parallel_invoke algorithm runs tasks concurrently, the order of the output messages could vary.

アルゴリズムの使用方法を示す完全な例につい parallel_invoke ては、「方法: Parallel_invoke を使用して並列並べ替えルーチンを記述する」および「方法: parallel_invoke を使用して並列操作を実行する」を参照してください。For complete examples that show how to use the parallel_invoke algorithm, see How to: Use parallel_invoke to Write a Parallel Sort Routine and How to: Use parallel_invoke to Execute Parallel Operations. クラスを使用して非同期のフューチャを実装する完全な例につい task_group ては、「チュートリアル: フューチャの実装」を参照してください。For a complete example that uses the task_group class to implement asynchronous futures, see Walkthrough: Implementing Futures.

堅牢なプログラミングRobust Programming

タスク、タスク グループ、および並列アルゴリズムを使用する場合は、キャンセル処理と例外処理の役割を十分に理解しておいてください。Make sure that you understand the role of cancellation and exception handling when you use tasks, task groups, and parallel algorithms. たとえば、並列処理ツリーでタスクを取り消すと、子タスクも実行されなくなります。For example, in a tree of parallel work, a task that is canceled prevents child tasks from running. そのため、アプリケーションで重要となる操作 (リソースの解放など) が子タスクのいずれかで実行されるような場合に問題となります。This can cause problems if one of the child tasks performs an operation that is important to your application, such as freeing a resource. また、子タスクが例外をスローすると、その例外がオブジェクトのデストラクターを介して反映され、アプリケーションで未定義の動作が発生する可能性があります。In addition, if a child task throws an exception, that exception could propagate through an object destructor and cause undefined behavior in your application. これらの点を示す例については、「並列パターンライブラリ」ドキュメントのベストプラクティスの「キャンセルと例外処理がオブジェクトの破棄に与える影響について」を参照してください。For an example that illustrates these points, see the Understand how Cancellation and Exception Handling Affect Object Destruction section in the Best Practices in the Parallel Patterns Library document. PPL のキャンセルモデルと例外処理モデルの詳細については、「キャンセル例外処理」を参照してください。For more information about the cancellation and exception-handling models in the PPL, see Cancellation and Exception Handling.

TitleTitle [説明]Description
方法: parallel_invoke を使用して並列並べ替えルーチンを記述するHow to: Use parallel_invoke to Write a Parallel Sort Routine parallel_invoke アルゴリズムを使用して、バイトニック ソート アルゴリズムのパフォーマンスを向上させる方法について説明します。Shows how to use the parallel_invoke algorithm to improve the performance of the bitonic sort algorithm.
方法: parallel_invoke を使用して並列操作を実行するHow to: Use parallel_invoke to Execute Parallel Operations parallel_invoke アルゴリズムを使用して、共有データ ソースに対して複数の操作を実行するプログラムのパフォーマンスを向上させる方法について説明します。Shows how to use the parallel_invoke algorithm to improve the performance of a program that performs multiple operations on a shared data source.
方法: 遅延後に完了するタスクを作成するHow to: Create a Task that Completes After a Delay 、、、およびの各クラスを使用して taskcancellation_token_source cancellation_token task_completion_event 遅延後に完了するタスクを作成する方法について説明します。Shows how to use the task, cancellation_token_source, cancellation_token, and task_completion_event classes to create a task that completes after a delay.
チュートリアル: フューチャの実装Walkthrough: Implementing Futures コンカレンシー ランタイムの既存の機能を組み合わせて、より効果的に使用する方法を示します。Shows how to combine existing functionality in the Concurrency Runtime into something that does more.
並列パターン ライブラリ (PPL)Parallel Patterns Library (PPL) 同時実行アプリケーションの開発に不可欠なプログラミング モデルを提供する PPL について説明します。Describes the PPL, which provides an imperative programming model for developing concurrent applications.

リファレンスReference

task クラス (同時実行ランタイム)task Class (Concurrency Runtime)

task_completion_event クラスtask_completion_event Class

when_all 関数when_all Function

when_any 関数when_any Function

task_group クラスtask_group Class

parallel_invoke 関数parallel_invoke Function

structured_task_group クラスstructured_task_group Class