C++ での UWP アプリ用の非同期操作の作成Creating Asynchronous Operations in C++ for UWP Apps

このドキュメントでは、task クラスを使用してユニバーサル Windows ランタイム (UWP) アプリで Windows ThreadPool ベースの非同期操作を生成する際に留意すべき重要な点について説明します。This document describes some of the key points to keep in mind when you use the task class to produce Windows ThreadPool-based asynchronous operations in a Universal Windows Runtime (UWP) app.

非同期プログラミングの使用は、アプリがユーザー入力に対する応答性を維持できるようにするため、Windows ランタイムアプリモデルの重要なコンポーネントです。The use of asynchronous programming is a key component in the Windows Runtime app model because it enables apps to remain responsive to user input. UI スレッドをブロックすることなく、長時間実行されるタスクを開始することができ、後でタスクの結果を受け取ることができます。You can start a long-running task without blocking the UI thread, and you can receive the results of the task later. また、タスクを取り消したり、タスクがバックグラウンドで実行される間に進行状況の通知を受け取ることができます。You can also cancel tasks and receive progress notifications as tasks run in the background. ドキュメント「 C++ での非同期プログラミング」では、UWP アプリを作成するために Visual C++ で使用できる非同期パターンの概要を説明しています。The document Asynchronous programming in C++ provides an overview of the asynchronous pattern that's available in Visual C++ to create UWP apps. このドキュメントでは、非同期 Windows ランタイム操作のチェーンを使用して作成する方法について説明します。That document teaches how to both consume and create chains of asynchronous Windows Runtime operations. このセクションでは、ppltasks.h の型を使用して、別の Windows ランタイムコンポーネントで使用できる非同期操作を生成する方法と、非同期処理の実行方法を制御する方法について説明します。This section describes how to use the types in ppltasks.h to produce asynchronous operations that can be consumed by another Windows Runtime component and how to control how asynchronous work is executed. また、Hilo での非同期プログラミングパターンとヒント (c++ と xaml を使用した Windows ストアアプリ)を使用して、hilo で非同期操作を実装する方法、および C++ および xaml を使用する Windows ランタイムアプリについても説明します。Also consider reading Async programming patterns and tips in Hilo (Windows Store apps using C++ and XAML) to learn how we used the task class to implement asynchronous operations in Hilo, a Windows Runtime app using C++ and XAML.

注意

UWP アプリでは、並列パターンライブラリ(PPL) と非同期エージェントライブラリを使用できます。You can use the Parallel Patterns Library (PPL) and Asynchronous Agents Library in a UWP app. ただし、タスク スケジューラとリソース マネージャーを使用することはできません。However, you cannot use the Task Scheduler or the Resource Manager. このドキュメントでは、PPL が提供するその他の機能について説明します。これは、デスクトップアプリではなく、UWP アプリでのみ使用できます。This document describes additional features that the PPL provides that are available only to a UWP app, and not to a desktop app.

重要なポイントKey points

  • 他のコンポーネントで使用する非同期操作 (C++ 以外の言語で記述される場合があります) を作成するには、concurrency::create_async を使用します。Use concurrency::create_async to create asynchronous operations that can be used by other components (which might be written in languages other than C++).

  • 非同期操作を呼び出すコンポーネントに進行状況の通知をレポートするためには、concurrency::progress_reporter を使用します。Use concurrency::progress_reporter to report progress notifications to components that call your asynchronous operations.

  • 内部非同期操作の取り消しを可能にするには、キャンセル トークンを使用します。Use cancellation tokens to enable internal asynchronous operations to cancel.

  • create_async の関数の動作は、渡される処理関数の戻り値の型によって異なります。The behavior of the create_async function depends on the return type of the work function that is passed to it. タスク ( task<T> または task<void>) を返す処理関数は、 create_asyncを呼び出したコンテキストで同期的に実行されます。A work function that returns a task (either task<T> or task<void>) runs synchronously in the context that called create_async. T任意のコンテキストでを返すか、または実行する処理関数 voidA work function that returns T or void runs in an arbitrary context.

  • concurrency::task::then メソッドを使用すると、順次実行タスクのチェーンを作成できます。You can use the concurrency::task::then method to create a chain of tasks that run one after another. UWP アプリでは、タスクの継続の既定のコンテキストは、そのタスクがどのように構築されたかによって異なります。In a UWP app, the default context for a task's continuations depends on how that task was constructed. 非同期アクションをタスク コンストラクターに渡すことによってタスクが作成されている場合、または非同期アクションを返すラムダ式を渡すことによってタスクが作成されている場合は、そのタスクのすべての継続の既定のコンテキストは、現在のコンテキストです。If the task was created by passing an asynchronous action to the task constructor, or by passing a lambda expression that returns an asynchronous action, then the default context for all continuations of that task is the current context. タスクが非同期アクションから構築されていない場合、既定ではタスクの継続に任意のコンテキストが使用されます。If the task is not constructed from an asynchronous action, then an arbitrary context is used by default for the task's continuations. 既定のコンテキストを concurrency::task_continuation_context クラスでオーバーライドできます。You can override the default context with the concurrency::task_continuation_context class.

このドキュメントの内容In this document

非同期操作の作成Creating Asynchronous Operations

並列パターン ライブラリ (PPL) でタスクや継続のモデルを使用して、バックグラウンド タスクを定義したり、前のタスクが完了すると実行される追加のタスクを定義することができます。You can use the task and continuation model in the Parallel Patterns Library (PPL) to define background tasks as well as additional tasks that run when the previous task completes. この機能は concurrency::task クラスによって提供されます。This functionality is provided by the concurrency::task class. このモデルの詳細および task クラスの詳細については、「 Task Parallelismを呼び出したコンテキストで同期的に実行されます。For more information about this model and the task class, see Task Parallelism.

Windows ランタイムは、特別なオペレーティングシステム環境でのみ実行される UWP アプリを作成するために使用できるプログラミングインターフェイスです。The Windows Runtime is a programming interface that you can use to create UWP apps that run only in a special operating system environment. このようなアプリは、承認された機能、データ型、およびデバイスを使用し、Microsoft Store から配布されます。Such apps use authorized functions, data types, and devices, and are distributed from the Microsoft Store. Windows ランタイムは、アプリケーションバイナリインターフェイス(ABI) によって表されます。The Windows Runtime is represented by the Application Binary Interface (ABI). ABI は、Visual C++ などのプログラミング言語で Windows ランタイム Api を使用できるようにする、基になるバイナリコントラクトです。The ABI is an underlying binary contract that makes Windows Runtime APIs available to programming languages such as Visual C++.

Windows ランタイムを使用すると、さまざまなプログラミング言語の最適な機能を使用して1つのアプリにまとめることができます。By using the Windows Runtime, you can use the best features of various programming languages and combine them into one app. たとえば、JavaScript で UI を作成し、C ++ のコンポーネントで計算量が非常に多い演算を行うことができます。For example, you might create your UI in JavaScript and perform the computationally-intensive app logic in a C++ component. 計算量が非常に多い演算をバックグラウンドで行うことができるのは、UI の応答性を保つための重要な要素です。The ability to perform these computationally-intensive operations in the background is a key factor in keeping your UI responsive. taskクラスは c++ に固有であるため、Windows ランタイムインターフェイスを使用して、非同期操作を他のコンポーネント (c++ 以外の言語で記述されている可能性があります) に伝達する必要があります。Because the task class is specific to C++, you must use a Windows Runtime interface to communicate asynchronous operations to other components (which might be written in languages other than C++). Windows ランタイムには、非同期操作を表すために使用できる4つのインターフェイスが用意されています。The Windows Runtime provides four interfaces that you can use to represent asynchronous operations:

Windows::Foundation::IAsyncActionWindows::Foundation::IAsyncAction
非同期アクションを表します。Represents an asynchronous action.

Windows:: Foundation:: IAsyncActionWithProgress<TProgress>Windows::Foundation::IAsyncActionWithProgress<TProgress>
進行状況を報告する非同期アクションを表します。Represents an asynchronous action that reports progress.

Windows:: Foundation:: IAsyncOperation<TResult>Windows::Foundation::IAsyncOperation<TResult>
結果を返す非同期操作を表します。Represents an asynchronous operation that returns a result.

Windows:: Foundation:: IAsyncOperationWithProgress<TResult, TProgress>Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>
結果を返し、進行状況を報告する、非同期操作を表します。Represents an asynchronous operation that returns a result and reports progress.

アクションの概念は、非同期タスクが値を生成しないことを意味します (を返す関数を考えてみ void ます)。The notion of an action means that the asynchronous task doesn't produce a value (think of a function that returns void). 操作 の概念は、非同期タスクが値を生成することを意味します。The notion of an operation means that the asynchronous task does produce a value. 進行状況 の概念は、タスクが呼び出し元に進行状況を報告できることを意味します。The notion of progress means that the task can report progress messages to the caller. JavaScript、.NET Framework および Visual C++ はそれぞれ、ABI の境界を越えて使用するため、これらのインターフェイスのインスタンスを作成する独自の方法を提供します。JavaScript, the .NET Framework, and Visual C++ each provides its own way to create instances of these interfaces for use across the ABI boundary. Visual C++ では、PPL は concurrency::create_async 関数を提供します。For Visual C++, the PPL provides the concurrency::create_async function. この関数は、タスクの完了を表す Windows ランタイム非同期アクションまたは操作を作成します。This function creates a Windows Runtime asynchronous action or operation that represents the completion of a task. 関数は、 create_async 処理関数 (通常はラムダ式) を受け取り、内部的にオブジェクトを作成 task し、4つの非同期 Windows ランタイムインターフェイスのいずれかでそのタスクをラップします。The create_async function takes a work function (typically a lambda expression), internally creates a task object, and wraps that task in one of the four asynchronous Windows Runtime interfaces.

注意

create_async別の言語または別の Windows ランタイムコンポーネントからアクセスできる機能を作成する必要がある場合にのみ、を使用します。Use create_async only when you have to create functionality that can be accessed from another language or another Windows Runtime component. 同じコンポーネントの C++ コードで操作が生成、実行されることがわかっている場合には、 task クラスを直接使用します。Use the task class directly when you know that the operation is both produced and consumed by C++ code in the same component.

create_async の戻り値の型は、引数の型によって決まります。The return type of create_async is determined by the type of its arguments. たとえば、作業関数が値を返さず、進行状況を報告しない場合、 create_asyncIAsyncActionを返します。For example, if your work function doesn't return a value and doesn't report progress, create_async returns IAsyncAction. 作業関数が値を返さず、進行状況を報告する場合、 create_asyncIAsyncActionWithProgressを返します。If your work function doesn't return a value and also reports progress, create_async returns IAsyncActionWithProgress. 進行状況を報告するには、作業関数のパラメーターとして concurrency::progress_reporter オブジェクトを提供します。To report progress, provide a concurrency::progress_reporter object as the parameter to your work function. 進行状況を報告する機能により、実行された作業量と残りの作業量を報告できます (たとえば、パーセントにより)。The ability to report progress enables you to report what amount of work was performed and what amount still remains (for example, as a percentage). 結果が使用できるようになったらそれを報告することができます。It also enables you to report results as they become available.

IAsyncActionIAsyncActionWithProgress<TProgress>IAsyncOperation<TResult>、および IAsyncActionOperationWithProgress<TProgress, TProgress> インターフェイスはそれぞれ、非同期操作を取り消すことができるように Cancel メソッドを提供しています。The IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, and IAsyncActionOperationWithProgress<TProgress, TProgress> interfaces each provide a Cancel method that enables you to cancel the asynchronous operation. task クラスは、キャンセル トークンを使用します。The task class works with cancellation tokens. 作業を取り消すためにキャンセル トークンを使用すると、ランタイムはそのトークンをサブスクライブする新しい作業を開始しません。When you use a cancellation token to cancel work, the runtime does not start new work that subscribes to that token. 既にアクティブである作業はそのキャンセル トークンを監視でき、可能な場合には停止できます。Work that is already active can monitor its cancellation token and stop when it can. この機構については、ドキュメント「 Cancellation in the PPL」で詳しく説明します。This mechanism is described in greater detail in the document Cancellation in the PPL. Windows ランタイムメソッドを使用してタスクのキャンセルを接続するには、 Cancel 次の2つの方法があります。You can connect task cancellation with the Windows RuntimeCancel methods in two ways. 最初に、create_async に渡す処理関数が concurrency::cancellation_token オブジェクトとなるように定義できます。First, you can define the work function that you pass to create_async to take a concurrency::cancellation_token object. Cancelメソッドが呼び出されると、このキャンセルトークンは取り消され、通常のキャンセル規則は呼び出しをサポートする基になるオブジェクトに適用され task create_async ます。When the Cancel method is called, this cancellation token is canceled and the normal cancellation rules apply to the underlying task object that supports the create_async call. cancellation_token オブジェクトを指定しない場合、基になる task オブジェクトが暗黙的に定義します。If you do not provide a cancellation_token object, the underlying task object defines one implicitly. 処理関数の取り消しに協調的に応答する必要がある場合は cancellation_token オブジェクトを定義します。Define a cancellation_token object when you need to cooperatively respond to cancellation in your work function. 例: c++ および xaml を使用した Windows ランタイムアプリでの実行の制御」では、カスタム Windows ランタイム C++ コンポーネントを使用する C# および xaml を使用するユニバーサル WINDOWS プラットフォーム (UWP) アプリでキャンセルを実行する方法の例を示します。The section Example: Controlling Execution in a Windows Runtime App with C++ and XAML shows an example of how to perform cancellation in a Universal Windows Platform (UWP) app with C# and XAML that uses a custom Windows Runtime C++ component.

警告

タスクの継続のチェーンでは、キャンセルトークンが取り消されたときに、常に状態をクリーンアップし、 concurrency:: cancel_current_taskを呼び出します。In a chain of task continuations, always clean up state and then call concurrency::cancel_current_task when the cancellation token is canceled. cancel_current_taskを呼び出す代わりにすぐに制御を返す場合は、操作は、取り消された状態でなく、完了の状態に遷移します。If you return early instead of calling cancel_current_task, the operation transitions to the completed state instead of the canceled state.

次の表では、アプリケーションの非同期操作を定義するために使用できる組み合わせを示します。The following table summarizes the combinations that you can use to define asynchronous operations in your app.

この Windows ランタイムインターフェイスを作成するにはTo create this Windows Runtime interface create_asyncReturn this type from create_async これらのパラメーターの型を処理関数に渡して、暗黙的なキャンセル トークンを使用するPass these parameter types to your work function to use an implicit cancellation token これらのパラメーターの型を処理関数に渡して、明示的なキャンセル トークンを使用するPass these parameter types to your work function to use an explicit cancellation token
IAsyncAction void もしくはtask<void>void or task<void> (なし)(none) (cancellation_token)(cancellation_token)
IAsyncActionWithProgress<TProgress> void もしくはtask<void>void or task<void> (progress_reporter)(progress_reporter) (progress_reporter, cancellation_token)(progress_reporter, cancellation_token)
IAsyncOperation<TResult> T または task<T>T or task<T> (なし)(none) (cancellation_token)(cancellation_token)
IAsyncActionOperationWithProgress<TProgress, TProgress> T または task<T>T or task<T> (progress_reporter)(progress_reporter) (progress_reporter, cancellation_token)(progress_reporter, cancellation_token)

task 関数に渡す処理関数から、値または create_async オブジェクトを返すことができます。You can return a value or a task object from the work function that you pass to the create_async function. これらのバリエーションによって異なる動作になります。These variations produce different behaviors. 値が返されたら、処理関数はバックグラウンド スレッドで実行できるように、 task にラップされます。When you return a value, the work function is wrapped in a task so that it can be run on a background thread. また、基になる task は、暗黙的なキャンセル トークンを使用します。In addition, the underlying task uses an implicit cancellation token. 逆に task オブジェクトを返した場合、処理関数は同期的に実行されます。Conversely, if you return a task object, the work function runs synchronously. したがって task オブジェクトを返す場合は、処理関数で時間のかかる操作はすべてタスクとして実行され、アプリケーションの応答性を保てるようにします。Therefore, if you return a task object, ensure that any lengthy operations in your work function also run as tasks to enable your app to remain responsive. また、基になる task は、暗黙的なキャンセル トークンを使用しません。In addition, the underlying task does not use an implicit cancellation token. したがって、 cancellation_token から task オブジェクトを返す場合に取り消しをサポートする必要がある場合、 create_asyncオブジェクトを受け取るように処理関数を定義する必要があります。Therefore, you need to define your work function to take a cancellation_token object if you require support for cancellation when you return a task object from create_async.

次の例は、 IAsyncAction 別の Windows ランタイムコンポーネントで使用できるオブジェクトを作成するさまざまな方法を示しています。The following example shows the various ways to create an IAsyncAction object that can be consumed by another Windows Runtime component.

// Creates an IAsyncAction object and uses an implicit cancellation token.
auto op1 = create_async([]
{
    // Define work here.
});

// Creates an IAsyncAction object and uses no cancellation token.
auto op2 = create_async([]
{
    return create_task([]
    {
        // Define work here.
    });
});

// Creates an IAsyncAction object and uses an explicit cancellation token.
auto op3 = create_async([](cancellation_token ct)
{
    // Define work here.
});

// Creates an IAsyncAction object that runs another task and also uses an explicit cancellation token.
auto op4 = create_async([](cancellation_token ct)
{
    return create_task([ct]()
    {
        // Define work here.
    });
});

例: C++ Windows ランタイムコンポーネントの作成と C からの使用#Example: Creating a C++ Windows Runtime Component and Consuming it from C#

多くのコンピューティング処理を要する操作を実行するために、XAML と C# を使用して UI と C++ Windows ランタイムコンポーネントを定義するアプリを考えてみましょう。Consider an app that uses XAML and C# to define the UI and a C++ Windows Runtime component to perform compute-intensive operations. この例では、C++ コンポーネントは特定の範囲での素数を計算します。In this example, the C++ component computes which numbers in a given range are prime. 4つの Windows ランタイム非同期タスクインターフェイスの違いを説明するために、Visual Studio で、空のソリューションを作成して名前を付けることによって、を開始し Primes ます。To illustrate the differences among the four Windows Runtime asynchronous task interfaces, start, in Visual Studio, by creating a Blank Solution and naming it Primes. 次に、ソリューションに [Windows ランタイム コンポーネント] プロジェクトを追加し、名前を PrimesLibraryとします。Then add to the solution a Windows Runtime Component project and naming it PrimesLibrary. 生成された C++ ヘッダー ファイル (この例では Class1.h の名前を Primes.h に変更しています) に次のコードを追加します。Add the following code to the generated C++ header file (this example renames Class1.h to Primes.h). public メソッドは、4つの非同期インターフェイスのいずれかを定義します。Each public method defines one of the four asynchronous interfaces. 値を返すメソッドは、 Windows:: Foundation:: Collections:: IVector <int> オブジェクトを返します。The methods that return a value return a Windows::Foundation::Collections::IVector<int> object. 進行状況を報告するメソッドは、 double 完了した作業全体の割合を定義する値を生成します。The methods that report progress produce double values that define the percentage of overall work that has completed.

#pragma once

namespace PrimesLibrary
{
    public ref class Primes sealed
    {
    public:
        Primes();

        // Computes the numbers that are prime in the provided range and stores them in an internal variable.
        Windows::Foundation::IAsyncAction^ ComputePrimesAsync(int first, int last);

        // Computes the numbers that are prime in the provided range and stores them in an internal variable.
        // This version also reports progress messages.
        Windows::Foundation::IAsyncActionWithProgress<double>^ ComputePrimesWithProgressAsync(int first, int last);

        // Gets the numbers that are prime in the provided range.
        Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<int>^>^ GetPrimesAsync(int first, int last);

        // Gets the numbers that are prime in the provided range. This version also reports progress messages.
        Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ GetPrimesWithProgressAsync(int first, int last);
    };
}

注意

慣例により、Windows ランタイムの非同期メソッド名は、通常、"Async" で終わります。By convention, asynchronous method names in the Windows Runtime typically end with "Async".

生成された C++ ソース ファイル (この例では Class1.cpp の名前を Primes.cpp に変更しています) に次のコードを追加します。Add the following code to the generated C++ source file (this example renames Class1.cpp to Primes.cpp). is_prime 関数は、入力が素数かどうかを判定します。The is_prime function determines whether its input is prime. 残りのメソッドは Primes クラスを実装します。The remaining methods implement the Primes class. create_async への呼び出しはそれぞれ、呼び出されるメソッドと互換性のあるシグネチャを使用します。Each call to create_async uses a signature that's compatible with the method from which it is called. たとえば、 Primes::ComputePrimesAsyncIAsyncActionを返すため、 create_async に指定された処理関数は値を返さず、パラメーターとして progress_reporter オブジェクトを受け取りません。For example, because Primes::ComputePrimesAsync returns IAsyncAction, the work function that's provided to create_async doesn't return a value and doesn't take a progress_reporter object as its parameter.

// PrimesLibrary.cpp
#include "pch.h"
#include "Primes.h"
#include <atomic>
#include <collection.h>
#include <ppltasks.h>
#include <concurrent_vector.h>

using namespace concurrency;
using namespace std;

using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;

using namespace PrimesLibrary;

Primes::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;
}

// Adds the numbers that are prime in the provided range  
// to the primes global variable.
IAsyncAction^ Primes::ComputePrimesAsync(int first, int last)
{
    return create_async([this, first, last]
    {
        // Ensure that the input values are in range. 
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        parallel_for(first, last + 1, [this](int n)
        {
            if (is_prime(n))
            {
                // Perhaps store the value somewhere...
            }
        });
    });
}

IAsyncActionWithProgress<double>^ Primes::ComputePrimesWithProgressAsync(int first, int last)
{
    return create_async([first, last](progress_reporter<double> reporter)
    {
        // Ensure that the input values are in range.
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel. 
        atomic<long> operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;
        parallel_for(first, last + 1, [&operation, range, &lastPercent, reporter](int n)
        {
            // Report progress message.
            double progress = 100.0 * (++operation) / range;
            if (progress >= lastPercent)
            {
                reporter.report(progress);
                lastPercent += 1.0;
            }

            if (is_prime(n))
            {
                // Perhaps store the value somewhere...
            }
        });
        reporter.report(100.0);
    });
}

IAsyncOperation<IVector<int>^>^ Primes::GetPrimesAsync(int first, int last)
{
    return create_async([this, first, last]() -> IVector<int>^
    {
        // Ensure that the input values are in range. 
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        concurrent_vector<int> primes;
        parallel_for(first, last + 1, [this, &primes](int n)
        {
            // If the value is prime, add it to the global vector.
            if (is_prime(n))
            {
                primes.push_back(n);
            }
        });
        // Sort the results.
        sort(begin(primes), end(primes), less<int>());

        // Copy the results to an IVector object. The IVector 
        // interface makes collections of data available to other 
        // Windows Runtime components.
        auto results = ref new Vector<int>();
        for (int prime : primes)
        {
            results->Append(prime);
        }
        return results;
    });
}

IAsyncOperationWithProgress<IVector<int>^, double>^ Primes::GetPrimesWithProgressAsync(int first, int last)
{
    return create_async([this, first, last](progress_reporter<double> reporter) -> IVector<int>^
    {
        // Ensure that the input values are in range.
        if (first < 0 || last < 0)
        {
            throw ref new InvalidArgumentException();
        }
        // Perform the computation in parallel.
        concurrent_vector<int> primes;
        long operation = 0;
        long range = last - first + 1;
        double lastPercent = 0.0;
        parallel_for(first, last + 1, [&primes, &operation, range, &lastPercent, reporter](int n)
        {
            // Report progress message.
            double progress = 100.0 * (++operation) / range;
            if (progress >= lastPercent)
            {
                reporter.report(progress);
                lastPercent += 1.0;
            }

            // If the value is prime, add it to the local vector. 
            if (is_prime(n))
            {
                primes.push_back(n);
            }
        });
        reporter.report(100.0);

        // Sort the results.
        sort(begin(primes), end(primes), less<int>());

        // Copy the results to an IVector object. The IVector 
        // interface makes collections of data available to other 
        // Windows Runtime components.
        auto results = ref new Vector<int>();
        for (int prime : primes)
        {
            results->Append(prime);
        }
        return results;
    });
}

各メソッドは最初に検証を実行して、入力パラメーターが負でないことを確認します。Each method first performs validation to ensure that the input parameters are non-negative. 入力値が負数の場合、メソッドは Platform::InvalidArgumentExceptionをスローします。If an input value is negative, the method throws Platform::InvalidArgumentException. エラー処理は、このセクションで後述します。Error handling is explained later in this section.

UWP アプリからこれらのメソッドを使用するには、Visual C# の [空のアプリ (XAML) ] テンプレートを使用して、visual Studio ソリューションに2つ目のプロジェクトを追加します。To consume these methods from a UWP app, use the Visual C# Blank App (XAML) template to add a second project to the Visual Studio solution. この例では、プロジェクトの名前を Primesとします。This example names the project Primes. 次に、 Primes プロジェクトから PrimesLibrary プロジェクトへの参照を追加します。Then, from the Primes project, add a reference to the PrimesLibrary project.

MainPage.xaml に次のコードを追加します。Add the following code to MainPage.xaml. このコードは C++ コンポーネントを呼び出して結果を表示する UI を定義します。This code defines the UI so that you can call the C++ component and display results.

<Page
    x:Class="Primes.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Primes"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300"/>
            <ColumnDefinition Width="300"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="125"/>
            <RowDefinition Height="125"/>
            <RowDefinition Height="125"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Column="0" Grid.Row="0">
            <Button Name="b1" Click="computePrimes">Compute Primes</Button>
            <TextBlock Name="tb1"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="1" Grid.Row="0">
            <Button Name="b2" Click="computePrimesWithProgress">Compute Primes with Progress</Button>
            <ProgressBar Name="pb1" HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb2"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="0" Grid.Row="1">
            <Button Name="b3" Click="getPrimes">Get Primes</Button>
            <TextBlock Name="tb3"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="1" Grid.Row="1">
            <Button Name="b4" Click="getPrimesWithProgress">Get Primes with Progress</Button>
            <ProgressBar Name="pb4"  HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb4"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="0" Grid.Row="2">
            <Button Name="b5" Click="getPrimesHandleErrors">Get Primes and Handle Errors</Button>
            <ProgressBar Name="pb5"  HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb5"></TextBlock>
        </StackPanel>

        <StackPanel Grid.Column="1" Grid.Row="2">
            <Button Name="b6" Click="getPrimesCancellation">Get Primes with Cancellation</Button>
            <Button Name="cancelButton" Click="cancelGetPrimes" IsEnabled="false">Cancel</Button>
            <ProgressBar Name="pb6"  HorizontalAlignment="Left" Width="100"></ProgressBar>
            <TextBlock Name="tb6"></TextBlock>
        </StackPanel>
    </Grid>
</Page>

MainPage.xaml で、次のコードを MainPage クラスに追加します。Add the following code to the MainPage class in MainPage.xaml. このコードは Primes オブジェクトとボタンのイベント ハンドラーを定義します。This code defines a Primes object and the button event handlers.

private PrimesLibrary.Primes primesLib = new PrimesLibrary.Primes();

private async void computePrimes(object sender, RoutedEventArgs e)
{
    b1.IsEnabled = false;
    tb1.Text = "Working...";

    var asyncAction = primesLib.ComputePrimesAsync(0, 100000);

    await asyncAction;

    tb1.Text = "Done";
    b1.IsEnabled = true;
}

private async void computePrimesWithProgress(object sender, RoutedEventArgs e)
{
    b2.IsEnabled = false;
    tb2.Text = "Working...";

    var asyncAction = primesLib.ComputePrimesWithProgressAsync(0, 100000);
    asyncAction.Progress = new AsyncActionProgressHandler<double>((action, progress) =>
    {
        pb1.Value = progress;
    });

    await asyncAction;

    tb2.Text = "Done";
    b2.IsEnabled = true;
}

private async void getPrimes(object sender, RoutedEventArgs e)
{
    b3.IsEnabled = false;
    tb3.Text = "Working...";

    var asyncOperation = primesLib.GetPrimesAsync(0, 100000);

    await asyncOperation;

    tb3.Text = "Found " + asyncOperation.GetResults().Count + " primes";
    b3.IsEnabled = true;
}

private async void getPrimesWithProgress(object sender, RoutedEventArgs e)
{
    b4.IsEnabled = false;
    tb4.Text = "Working...";

    var asyncOperation = primesLib.GetPrimesWithProgressAsync(0, 100000);
    asyncOperation.Progress = new AsyncOperationProgressHandler<IList<int>, double>((operation, progress) =>
    {
        pb4.Value = progress;
    });

    await asyncOperation;

    tb4.Text = "Found " + asyncOperation.GetResults().Count + " primes";
    b4.IsEnabled = true;
}

private async void getPrimesHandleErrors(object sender, RoutedEventArgs e)
{
    b5.IsEnabled = false;
    tb5.Text = "Working...";

    var asyncOperation = primesLib.GetPrimesWithProgressAsync(-1000, 100000);
    asyncOperation.Progress = new AsyncOperationProgressHandler<IList<int>, double>((operation, progress) =>
    {
        pb5.Value = progress;
    });

    try
    {
        await asyncOperation;
        tb5.Text = "Found " + asyncOperation.GetResults().Count + " primes";
    }
    catch (ArgumentException ex)
    {
        tb5.Text = "ERROR: " + ex.Message;
    }

    b5.IsEnabled = true;
}

private IAsyncOperationWithProgress<IList<int>, double> asyncCancelableOperation;

private async void getPrimesCancellation(object sender, RoutedEventArgs e)
{
    b6.IsEnabled = false;
    cancelButton.IsEnabled = true;
    tb6.Text = "Working...";

    asyncCancelableOperation = primesLib.GetPrimesWithProgressAsync(0, 200000);
    asyncCancelableOperation.Progress = new AsyncOperationProgressHandler<IList<int>, double>((operation, progress) =>
    {
        pb6.Value = progress;
    });

    try
    {
        await asyncCancelableOperation;
        tb6.Text = "Found " + asyncCancelableOperation.GetResults().Count + " primes";
    }
    catch (System.Threading.Tasks.TaskCanceledException)
    {
        tb6.Text = "Operation canceled";
    }

    b6.IsEnabled = true;
    cancelButton.IsEnabled = false;
}

private void cancelGetPrimes(object sender, RoutedEventArgs e)
{
    cancelButton.IsEnabled = false;
    asyncCancelableOperation.Cancel();
}

これらのメソッドは asyncawait のキーワードを使用して、非同期操作が完了した後で UI を更新します。These methods use the async and await keywords to update the UI after the asynchronous operations complete. UWP アプリの非同期コーディングの詳細については、「スレッド処理と非同期プログラミング」を参照してください。For information about asynchronous coding in UWP apps, see Threading and async programming.

getPrimesCancellation および cancelGetPrimes メソッドは連携して、ユーザーが操作の取り消しをできるようにします。The getPrimesCancellation and cancelGetPrimes methods work together to enable the user to cancel the operation. ユーザーが [キャンセル] ボタンを選択すると、 cancelGetPrimes メソッドはIAsyncOperationWithProgress <TResult, TProgress> :: cancelを呼び出して操作を取り消します。When the user chooses the Cancel button, the cancelGetPrimes method calls IAsyncOperationWithProgress<TResult, TProgress>::Cancel to cancel the operation. 基になる非同期操作を管理する同時実行ランタイムは、取り消しが完了したことを通知するために Windows ランタイムによってキャッチされる内部例外の種類をスローします。The Concurrency Runtime, which manages the underlying asynchronous operation, throws an internal exception type that's caught by the Windows Runtime to communicate that cancellation has completed. 取り消しモデルの詳細については、「キャンセル」を参照してください。For more information about the cancellation model, see Cancellation.

重要

PPL が操作をキャンセルした Windows ランタイムに正しく報告できるようにするには、この内部例外の種類をキャッチしないでください。To enable the PPL to correctly report to the Windows Runtime that it has canceled the operation, do not catch this internal exception type. これは、すべての例外 (catch (...)) をキャッチしないことを意味します。This means that you should also not catch all exceptions (catch (...)). すべての例外をキャッチする必要がある場合は、例外を再スローして、Windows ランタイムが取り消し操作を完了できるようにします。If you must catch all exceptions, rethrow the exception to ensure that the Windows Runtime can complete the cancellation operation.

次の図は、各オプションが選択された後の Primes アプリケーションを示しています。The following illustration shows the Primes app after each option has been chosen.

Windows ランタイム Primes アプリWindows Runtime Primes app

を使用して、 create_async 他の言語で使用できる非同期タスクを作成する例については、「 Bing マップトリップオプティマイザーのサンプルでの C++ の使用」を参照してください。For an example that uses create_async to create asynchronous tasks that can be consumed by other languages, see Using C++ in the Bing Maps Trip Optimizer sample.

実行スレッドの制御Controlling the Execution Thread

Windows ランタイムは COM スレッドモデルを使用します。The Windows Runtime uses the COM threading model. このモデルでは、オブジェクトは、同期を扱う方法によって、異なるアパートメント内でホストされます。In this model, objects are hosted in different apartments, depending on how they handle their synchronization. スレッド セーフなオブジェクトは、マルチスレッド アパートメント (MTA) でホストされます。Thread-safe objects are hosted in the multi-threaded apartment (MTA). 1 つのスレッドによりアクセスされる必要があるオブジェクトは、シングルスレッド アパートメント (STA) でホストされます。Objects that must be accessed by a single thread are hosted in a single-threaded apartment (STA).

UI があるアプリケーションでは、ASTA (アプリケーション STA) スレッドはウィンドウ メッセージをポンプする必要があり、STA によりホストされた UI コントロールを更新できるプロセスでの唯一のスレッドです。In an app that has a UI, the ASTA (Application STA) thread is responsible for pumping window messages and is the only thread in the process that can update the STA-hosted UI controls. これにより次の 2 つの結果を生じます。This has two consequences. 最初に、アプリケーションの応答性を保つためには、すべての CPU 負荷の高い操作および I/O 操作は ASTA のスレッドで実行しないようにします。First, to enable the app to remain responsive, all CPU-intensive and I/O operations should not be run on the ASTA thread. 第 2 に、バックグラウンド スレッドからの結果は、UI を更新する ASTA にマーシャリングされる必要があります。Second, results that come from background threads must be marshaled back to the ASTA to update the UI. C++ UWP アプリでは、 MainPage および他の XAML ページはすべて ATSA で実行されます。In a C++ UWP app, MainPage and other XAML pages all run on the ATSA. したがって、ASTA で宣言されるタスクの継続は、既定ではその場で実行されるため、継続の本体でコントロールを直接更新できます。Therefore, task continuations that are declared on the ASTA are run there by default so you can update controls directly in the continuation body. ただし、タスクが別のタスクの入れ子になっている場合、入れ子のタスクのすべての継続は MTA で実行されます。However, if you nest a task in another task, any continuations on that nested task run in the MTA. したがって、継続が実行されるコンテキストを明示的に指定するかどうかを検討する必要があります。Therefore, you need to consider whether to explicitly specify on what context these continuations run.

IAsyncOperation<TResult>など、非同期操作から作成されたタスクは、特別な意味を使用するので、スレッド処理の詳細に注意する必要はありません。A task that's created from an asynchronous operation, such as IAsyncOperation<TResult>, uses special semantics that can help you ignore the threading details. 操作はバックグラウンド スレッドで実行できますが (またはスレッドにまったくサポートされない場合もあります)、継続は既定では継続の操作を開始したアパートメントでの実行を保証されています (つまり task::thenを呼び出したアパートメントから実行されます)。Although an operation might run on a background thread (or it may not be backed by a thread at all), its continuations are by default guaranteed to run on the apartment that started the continuation operations (in other words, from the apartment that called task::then). concurrency::task_continuation_context クラスを使用して、継続の実行コンテキストを制御できます。You can use the concurrency::task_continuation_context class to control the execution context of a continuation. これらの静的ヘルパー メソッドを使用して task_continuation_context オブジェクトを作成します。Use these static helper methods to create task_continuation_context objects:

task_continuation_context オブジェクトを task::then メソッドに渡して、継続の実行コンテキストを明示的に制御できます。またはタスクを別のアパートメントに渡してから task::then メソッドを呼び出して、暗黙的に実行コンテキストを制御できます。You can pass a task_continuation_context object to the task::then method to explicitly control the execution context of the continuation or you can pass the task to another apartment and then call the task::then method to implicitly control the execution context.

重要

UWP アプリのメイン UI スレッドは STA で実行されるため、sta で作成した継続は既定で STA で実行されます。Because the main UI thread of UWP apps run under STA, continuations that you create on that STA by default run on the STA. したがって、MTA に作成した継続は MTA 内で実行されます。Accordingly, continuations that you create on the MTA run on the MTA.

次のセクションでは、ディスクからファイルを読み込み、そのファイルで最もよく使われている単語を検索し、結果を UI に表示するアプリケーションを示します。The following section shows an app that reads a file from disk, finds the most common words in that file, and then shows the results in the UI. UI を更新する最後の操作は、UI スレッドで発生します。The final operation, updating the UI, occurs on the UI thread.

重要

この動作は、UWP アプリに固有です。This behavior is specific to UWP apps. デスクトップ アプリケーションでは、継続が実行される場所を制御できません。For desktop apps, you do not control where continuations run. その代わりに、各継続が実行されるワーカー スレッドをスケジューラが選択します。Instead, the scheduler chooses a worker thread on which to run each continuation.

重要

STA で実行される継続の本体で concurrency::task::wait を呼び出さないでください。Do not call concurrency::task::wait in the body of a continuation that runs on the STA. そうしないと、このメソッドが現在のスレッドをブロックして、アプリケーションが応答しなくなる場合があるため、ランタイムは concurrency::invalid_operation をスローします。Otherwise, the runtime throws concurrency::invalid_operation because this method blocks the current thread and can cause the app to become unresponsive. ただし、タスク ベースの継続で継続元タスクの結果を受け取るために concurrency::task::get のメソッドを呼び出すことができます。However, you can call the concurrency::task::get method to receive the result of the antecedent task in a task-based continuation.

例: C++ および XAML を使用した Windows ランタイムアプリでの実行の制御Example: Controlling Execution in a Windows Runtime App with C++ and XAML

ディスクからファイルを読み込み、そのファイルで最もよく使われている単語を検索し、結果を UI に表示する C++ XAML アプリケーションを考えてみます。Consider a C++ XAML app that reads a file from disk, finds the most common words in that file, and then shows the results in the UI. このアプリを作成するには、Visual Studio で、空のアプリ (ユニバーサル Windows) プロジェクトを作成して名前を付け CommonWords ます。To create this app, start, in Visual Studio, by creating a Blank App (Universal Windows) project and naming it CommonWords. アプリケーション マニフェストで、 [ドキュメント ライブラリ] の機能を指定して、アプリケーションがドキュメント フォルダーにアクセスできるようにします。In your app manifest, specify the Documents Library capability to enable the app to access the Documents folder. また、アプリケーション マニフェストの宣言セクションにテキスト (.txt) ファイルの種類を追加します。Also add the Text (.txt) file type to the declarations section of the app manifest. アプリの機能と宣言の詳細については、「 Windows アプリのパッケージ化、配置、およびクエリ」を参照してください。For more information about app capabilities and declarations, see Packaging, deployment, and query of Windows apps.

Grid 要素と ProgressRing 要素を含めるように、MainPage.xaml の TextBlock 要素を更新します。Update the Grid element in MainPage.xaml to include a ProgressRing element and a TextBlock element. ProgressRing は操作が進行中であることを示し、 TextBlock は計算の結果を示します。The ProgressRing indicates that the operation is in progress and the TextBlock shows the results of the computation.

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <ProgressRing x:Name="Progress"/>
    <TextBlock x:Name="Results" FontSize="16"/>
</Grid>

次の #include ステートメントを .pchに追加します。Add the following #include statements to pch.h.

#include <sstream>
#include <ppltasks.h>
#include <concurrent_unordered_map.h>

次のメソッド宣言を MainPage クラス (MainPage.h) に追加します。Add the following method declarations to the MainPage class (MainPage.h).

private:
    // Splits the provided text string into individual words.
    concurrency::task<std::vector<std::wstring>> MakeWordList(Platform::String^ text);

    // Finds the most common words that are at least the provided minimum length.
    concurrency::task<std::vector<std::pair<std::wstring, size_t>>> FindCommonWords(const std::vector<std::wstring>& words, size_t min_length, size_t count);

    // Shows the most common words on the UI.
    void ShowResults(const std::vector<std::pair<std::wstring, size_t>>& commonWords);

次の using ステートメントを mainpage.xaml に追加します。Add the following using statements to MainPage.cpp.

using namespace concurrency;
using namespace std;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;

MainPage.cpp で、 MainPage::MakeWordListMainPage::FindCommonWords、および MainPage::ShowResults のメソッドを実装します。In MainPage.cpp, implement the MainPage::MakeWordList, MainPage::FindCommonWords, and MainPage::ShowResults methods. MainPage::MakeWordListMainPage::FindCommonWords は計算量が非常に多い演算操作を行います。The MainPage::MakeWordList and MainPage::FindCommonWords perform computationally-intensive operations. MainPage::ShowResults メソッドは演算の結果を UI に表示します。The MainPage::ShowResults method displays the result of the computation in the UI.

// Splits the provided text string into individual words.
task<vector<wstring>> MainPage::MakeWordList(String^ text)
{
    return create_task([text]() -> vector<wstring>
    {
        vector<wstring> words;

        // Add continuous sequences of alphanumeric characters to the string vector.
        wstring current_word;
        for (wchar_t ch : text)
        {
            if (!iswalnum(ch))
            {
                if (current_word.length() > 0)
                {
                    words.push_back(current_word);
                    current_word.clear();
                }
            }
            else
            {
                current_word += ch;
            }
        }

        return words;
    });
}

// Finds the most common words that are at least the provided minimum length.
task<vector<pair<wstring, size_t>>> MainPage::FindCommonWords(const vector<wstring>& words, size_t min_length, size_t count)
{
    return create_task([words, min_length, count]() -> vector<pair<wstring, size_t>>
    {
        typedef pair<wstring, size_t> pair;

        // Counts the occurrences of each word.
        concurrent_unordered_map<wstring, size_t> counts;

        parallel_for_each(begin(words), end(words), [&counts, min_length](const wstring& word)
        {
            // Increment the count of words that are at least the minimum length. 
            if (word.length() >= min_length)
            {
                // Increment the count.
                InterlockedIncrement(&counts[word]);
            }
        });

        // Copy the contents of the map to a vector and sort the vector by the number of occurrences of each word.
        vector<pair> wordvector;
        copy(begin(counts), end(counts), back_inserter(wordvector));

        sort(begin(wordvector), end(wordvector), [](const pair& x, const pair& y)
        {
            return x.second > y.second;
        });

        size_t size = min(wordvector.size(), count);
        wordvector.erase(begin(wordvector) + size, end(wordvector));

        return wordvector;
    });
}

// Shows the most common words on the UI. 
void MainPage::ShowResults(const vector<pair<wstring, size_t>>& commonWords)
{
    wstringstream ss;
    ss << "The most common words that have five or more letters are:";
    for (auto commonWord : commonWords)
    {
        ss << endl << commonWord.first << L" (" << commonWord.second << L')';
    }

    // Update the UI.
    Results->Text = ref new String(ss.str().c_str());
}

MainPage コンストラクターを変更し、Homer による著書 The Iliad でよく使われている単語を UI に表示する継続タスクのチェーンを作成します。Modify the MainPage constructor to create a chain of continuation tasks that displays in the UI the common words in the book The Iliad by Homer. 最初の 2 つの継続タスクは、テキストを個々の単語に分割し、よく使われている単語を検索します。これには時間がかかるため、バックグラウンドで実行されるように明示的に設定されています。The first two continuation tasks, which split the text into individual words and find common words, can be time consuming and are therefore explicitly set to run in the background. 最後の継続タスクは UI を更新します。これは継続コンテキストを指定していないので、アパートメント スレッドの規則に従います。The final continuation task, which updates the UI, specifies no continuation context, and therefore follows the apartment threading rules.

MainPage::MainPage()
{
    InitializeComponent();

    // To run this example, save the contents of http://www.gutenberg.org/files/6130/6130-0.txt to your Documents folder.
    // Name the file "The Iliad.txt" and save it under UTF-8 encoding.

    // Enable the progress ring.
    Progress->IsActive = true;

    // Find the most common words in the book "The Iliad".

    // Get the file.
    create_task(KnownFolders::DocumentsLibrary->GetFileAsync("The Iliad.txt")).then([](StorageFile^ file)
    {
        // Read the file text.
        return FileIO::ReadTextAsync(file, UnicodeEncoding::Utf8);

        // By default, all continuations from a Windows Runtime async operation run on the 
        // thread that calls task.then. Specify use_arbitrary to run this continuation 
        // on a background thread.
    }, task_continuation_context::use_arbitrary()).then([this](String^ file)
    {
        // Create a word list from the text.
        return MakeWordList(file);

        // By default, all continuations from a Windows Runtime async operation run on the 
        // thread that calls task.then. Specify use_arbitrary to run this continuation 
        // on a background thread.
    }, task_continuation_context::use_arbitrary()).then([this](vector<wstring> words)
    {
        // Find the most common words.
        return FindCommonWords(words, 5, 9);

        // By default, all continuations from a Windows Runtime async operation run on the 
        // thread that calls task.then. Specify use_arbitrary to run this continuation 
        // on a background thread.
    }, task_continuation_context::use_arbitrary()).then([this](vector<pair<wstring, size_t>> commonWords)
    {
        // Stop the progress ring.
        Progress->IsActive = false;

        // Show the results.
        ShowResults(commonWords);

        // We don't specify a continuation context here because we want the continuation 
        // to run on the STA thread.
    });
}

注意

この例では、実行コンテキストを指定する方法と、継続のチェーンを構成する方法を示します。This example demonstrates how to specify execution contexts and how to compose a chain of continuations. 非同期操作から作成されたタスクは、既定では task::thenを呼び出したアパートメントで継続を実行することを思い出してください。Recall that by default a task that's created from an asynchronous operation runs its continuations on the apartment that called task::then. したがって、この例では task_continuation_context::use_arbitrary を使用して、UI が含まれていない操作をバックグラウンド スレッドで実行するように指定しています。Therefore, this example uses task_continuation_context::use_arbitrary to specify that operations that do not involve the UI be performed on a background thread.

次の図は CommonWords アプリケーションの結果を示しています。The following illustration shows the results of the CommonWords app.

CommonWords アプリの Windows ランタイムWindows Runtime CommonWords app

この例では、を task サポートするオブジェクトが暗黙的なキャンセルトークンを使用するため、キャンセルをサポートでき create_async ます。In this example, it's possible to support cancellation because the task objects that support create_async use an implicit cancellation token. タスクが協調的に取り消しに応答する必要がある場合には、 cancellation_token オブジェクトを受け取るように処理関数を定義します。Define your work function to take a cancellation_token object if your tasks need to respond to cancellation in a cooperative manner. PPL での取り消し処理の詳細については、「 Cancellation in the PPL」を参照してください。For more info about cancellation in the PPL, see Cancellation in the PPL

関連項目See also

コンカレンシー ランタイムConcurrency Runtime