C++/CX での非同期プログラミングAsynchronous programming in C++/CX

注意

このトピックは、C++/CX アプリケーションの管理ができるようにすることを目的としています。This topic exists to help you maintain your C++/CX application. ただし、新しいアプリケーションには C++/WinRT を使用することをお勧めします。But we recommend that you use C++/WinRT for new applications. C++/WinRT は Windows ランタイム (WinRT) API の標準的な最新の C++17 言語プロジェクションで、ヘッダー ファイル ベースのライブラリとして実装され、最新の Windows API への最上位アクセス権を提供するように設計されています。C++/WinRT is an entirely standard modern C++17 language projection for Windows Runtime (WinRT) APIs, implemented as a header-file-based library, and designed to provide you with first-class access to the modern Windows API.

この記事では VisualC コンポーネント拡張機能での非同期メソッドに推奨される方法について説明します。 (、C++/cli CX) を使用して、taskクラスで定義されている、 concurrency ppltasks.h で名前空間です。This article describes the recommended way to consume asynchronous methods in VisualC++ component extensions (C++/CX) by using the task class that's defined in the concurrency namespace in ppltasks.h.

ユニバーサル Windows プラットフォーム (UWP) の非同期型Universal Windows Platform (UWP) asynchronous types

ユニバーサル Windows プラットフォーム (UWP) には、非同期メソッドを呼び出すためのモデルが明確に定義されており、非同期メソッドを使う必要がある型があります。The Universal Windows Platform (UWP) features a well-defined model for calling asynchronous methods and provides the types that you need to consume such methods. UWP の非同期モデルについて詳しくない場合は、この記事の前に「非同期プログラミング」をご覧ください。If you are not familiar with the UWP asynchronous model, read Asynchronous Programming before you read the rest of this article.

非同期 UWP API は C++ で直接使うこともできますが、task class とそれに関連する型と関数を使うことをお勧めします。これは concurrency 名前空間のクラスで、<ppltasks.h> で定義されています。Although you can consume the asynchronous UWP APIs directly in C++, the preferred approach is to use the task class and its related types and functions, which are contained in the concurrency namespace and defined in <ppltasks.h>. concurrency::task は汎用型のクラスですが、/ZW コンパイラ スイッチ (ユニバーサル Windows プラットフォーム (UWP) アプリとそのコンポーネントには必須) を使うと、task クラスで UWP の非同期型をカプセル化して次の処理を簡単に行うことができます。The concurrency::task is a general-purpose type, but when the /ZW compiler switch—which is required for Universal Windows Platform (UWP) apps and components—is used, the task class encapsulates the UWP asynchronous types so that it's easier to:

  • 複数の非同期操作や同期操作を 1 つのチェーンで連結するchain multiple asynchronous and synchronous operations together

  • タスク チェーンで例外を処理するhandle exceptions in task chains

  • タスク チェーンで取り消しを実行するperform cancellation in task chains

  • 各タスクを適切なスレッド コンテキストまたはスレッド アパートメントで実行するensure that individual tasks run in the appropriate thread context or apartment

ここでは、task クラスを UWP の非同期 API で使う方法に関する基本的なガイダンスを示しています。This article provides basic guidance about how to use the task class with the UWP asynchronous APIs. task クラスと、create_task を含む関連メソッドの詳細については、タスクの並列処理 (同時実行ランタイム) を参照してください。For more complete documentation about task and its related methods including create_task, see Task Parallelism (Concurrency Runtime).

タスクを使った非同期操作の使用Consuming an async operation by using a task

次の例は、task クラスを使って、IAsyncOperation インターフェイスを返す async メソッドを使う方法を示しています。この操作では値が生成されます。The following example shows how to use the task class to consume an async method that returns an IAsyncOperation interface and whose operation produces a value. 基本的な手順は次のとおりです。Here are the basic steps:

  1. create_task メソッドを呼び出し、IAsyncOperation オブジェクトに渡します。Call the create_task method and pass it the IAsyncOperation^ object.

  2. タスクのメンバー関数 task::then を呼び出し、非同期操作の完了時に呼び出すラムダを指定します。Call the member function task::then on the task and supply a lambda that will be invoked when the asynchronous operation completes.

#include <ppltasks.h>
using namespace concurrency;
using namespace Windows::Devices::Enumeration;
...
void App::TestAsync()
{    
    //Call the *Async method that starts the operation.
    IAsyncOperation<DeviceInformationCollection^>^ deviceOp =
        DeviceInformation::FindAllAsync();

    // Explicit construction. (Not recommended)
    // Pass the IAsyncOperation to a task constructor.
    // task<DeviceInformationCollection^> deviceEnumTask(deviceOp);

    // Recommended:
    auto deviceEnumTask = create_task(deviceOp);

    // Call the task's .then member function, and provide
    // the lambda to be invoked when the async operation completes.
    deviceEnumTask.then( [this] (DeviceInformationCollection^ devices )
    {       
        for(int i = 0; i < devices->Size; i++)
        {
            DeviceInformation^ di = devices->GetAt(i);
            // Do something with di...          
        }       
    }); // end lambda
    // Continue doing work or return...
}

task::then 関数で作成されて返されるタスクのことを後続タスクと呼びます。The task that's created and returned by the task::then function is known as a continuation. ユーザーが指定するラムダの入力引数は (この例の場合)、タスク操作の完了時に生成される結果です。The input argument (in this case) to the user-provided lambda is the result that the task operation produces when it completes. この値は、IAsyncOperation インターフェイスを直接使う場合に IAsyncOperation::GetResults を呼び出して取得する値と同じになります。It's the same value that would be retrieved by calling IAsyncOperation::GetResults if you were using the IAsyncOperation interface directly.

task::then メソッドからは直ちに制御が返され、そのデリゲートは非同期作業が正常に完了するまで実行されません。The task::then method returns immediately, and its delegate doesn't run until the asynchronous work completes successfully. この例では、非同期操作で例外がスローされるか、取り消し要求によって非同期操作が取り消された状態で終わると、継続は実行されません。In this example, if the asynchronous operation causes an exception to be thrown, or ends in the canceled state as a result of a cancellation request, the continuation will never execute. 前のタスクが取り消されるか失敗しても実行される継続を記述する方法については後で説明します。Later, we’ll describe how to write continuations that execute even if the previous task was cancelled or failed.

タスクの変数はローカル スタックで宣言しますが、その有効期間は、操作が完了する前にメソッドから制御が返されても、すべての操作が完了してすべての参照がスコープ外になるまで削除されないように管理されます。Although you declare the task variable on the local stack, it manages its lifetime so that it is not deleted until all of its operations complete and all references to it go out of scope, even if the method returns before the operations complete.

タスクのチェーンの作成Creating a chain of tasks

非同期プログラミングでは、前のタスクが完了した場合にのみ継続が実行されるように、操作のシーケンス (タスク チェーン) を定義するのが一般的です。In asynchronous programming, it's common to define a sequence of operations, also known as task chains, in which each continuation executes only when the previous one completes. 場合によっては、前のタスク (先行タスク) で生成された値を後続タスクが入力として受け取ることもあります。In some cases, the previous (or antecedent) task produces a value that the continuation accepts as input. task::then メソッドを使うと、直観的な方法で簡単にタスク チェーンを作成できます。このメソッドは、task (T はラムダ関数の戻り値の型) を返します。By using the task::then method, you can create task chains in an intuitive and straightforward manner; the method returns a task where T is the return type of the lambda function. 複数の継続を含めて 1 つのタスク チェーンを構成することができます。You can compose multiple continuations into a task chain: myTask.then(…).then(…).then(…);

タスク チェーンは、継続で新しい非同期操作を作成する場合に特に便利です。このようなタスクのことを非同期タスクと呼びます。Task chains are especially useful when a continuation creates a new asynchronous operation; such a task is known as an asynchronous task. 次の例は、2 つの継続を含むタスク チェーンを示しています。The following example illustrates a task chain that has two continuations. 既存のファイルへのハンドルを取得する最初のタスクの操作が完了すると、1 つ目の継続でそのファイルを削除する新しい非同期操作が始まります。The initial task acquires the handle to an existing file, and when that operation completes, the first continuation starts up a new asynchronous operation to delete the file. その操作が完了すると、2 つ目の継続が実行され、確認メッセージが出力されます。When that operation completes, the second continuation runs, and outputs a confirmation message.

#include <ppltasks.h>
using namespace concurrency;
...
void App::DeleteWithTasks(String^ fileName)
{    
    using namespace Windows::Storage;
    StorageFolder^ localFolder = ApplicationData::Current->LocalFolder;
    auto getFileTask = create_task(localFolder->GetFileAsync(fileName));

    getFileTask.then([](StorageFile^ storageFileSample) ->IAsyncAction^ {       
        return storageFileSample->DeleteAsync();
    }).then([](void) {
        OutputDebugString(L"File deleted.");
    });
}

この例で重要なポイントは次の 4 つです。The previous example illustrates four important points:

  • 1 つ目の継続は、IAsyncAction^ オブジェクトを task に変換し、task を返します。The first continuation converts the IAsyncAction^ object to a task and returns the task.

  • 2 つ目の継続は、エラー処理を実行しないため、task ではなく void を入力として受け取ります。The second continuation performs no error handling, and therefore takes void and not task as input. これは値ベースの継続です。It is a value-based continuation.

  • 2 つ目の後続タスクは、DeleteAsync 操作が完了するまで実行されません。The second continuation doesn't execute until the DeleteAsync operation completes.

  • 2 つ目の後続タスクは値ベースであるため、DeleteAsync を呼び出して開始された操作から例外がスローされると、2 つ目の後続タスクは実行されません。Because the second continuation is value-based, if the operation that was started by the call to DeleteAsync throws an exception, the second continuation doesn't execute at all.

注: 非同期操作を作成する、 taskクラスを使用する方法の 1 つは、タスク チェーンを作成します。NoteCreating a task chain is just one of the ways to use the task class to compose asynchronous operations. 結合演算子 (&&) や選択演算子 (||) を使って操作を構成することもできます。You can also compose operations by using join and choice operators && and ||. 詳しくは、「タスクの並列処理 (同時実行ランタイム)」をご覧ください。For more information, see Task Parallelism (Concurrency Runtime).

ラムダ関数の戻り値の型とタスクの戻り値の型Lambda function return types and task return types

継続タスクでは、ラムダ関数の戻り値の型が task オブジェクトでラップされます。In a task continuation, the return type of the lambda function is wrapped in a task object. ラムダが double を返す場合、継続タスクの型は task になります。If the lambda returns a double, then the type of the continuation task is task. ただし、タスク オブジェクトは、戻り値の型を必要以上に入れ子にしないように設計されています。However, the task object is designed so that it doesn't produce needlessly nested return types. ラムダが IAsyncOperation<SyndicationFeed^>^ を返す場合、継続は、task<task<SyndicationFeed^>>task<IAsyncOperation<SyndicationFeed^>^>^ ではなく、task<SyndicationFeed^> を返します。If a lambda returns an IAsyncOperation<SyndicationFeed^>^, the continuation returns a task<SyndicationFeed^>, not a task<task<SyndicationFeed^>> or task<IAsyncOperation<SyndicationFeed^>^>^. 非同期ラップ解除と呼ばれるこの処理により、さらに、継続内の非同期操作が完了しないと次の継続が呼び出されないようになります。This process is known as asynchronous unwrapping and it also ensures that the asynchronous operation inside the continuation completes before the next continuation is invoked.

前の例では、ラムダが IAsyncInfo オブジェクトを返しているのに、タスクは task を返しています。In the previous example, notice that the task returns a task even though its lambda returned an IAsyncInfo object. ラムダ関数とその外側のタスクの間で行われるこれらの型変換を次の表に示します。The following table summarizes the type conversions that occur between a lambda function and the enclosing task:

ラムダの戻り値の型lambda return type .then の戻り値の型return type
TResultTResult tasktask
IAsyncOperation^IAsyncOperation^ tasktask
IAsyncOperationWithProgress<TResult, TProgress>^IAsyncOperationWithProgress<TResult, TProgress>^ tasktask
IAsyncAction^IAsyncAction^ tasktask
IAsyncActionWithProgress^IAsyncActionWithProgress^ tasktask
tasktask tasktask

タスクの取り消しCanceling tasks

通常、非同期操作をユーザーが取り消せるようにすることをお勧めします。It is often a good idea to give the user the option to cancel an asynchronous operation. また、場合によっては、プログラムを使ってタスク チェーンの外側から操作を取り消さなければならないこともあります。And in some cases you might have to cancel an operation programmatically from outside the task chain. *Async* のそれぞれの戻り値の型には IAsyncInfo から継承した Cancel メソッドが含まれますが、それを外部のメソッドに公開する方法はあまりお勧めできません。Although each *Async return type has a Cancel method that it inherits from IAsyncInfo, it's awkward to expose it to outside methods. タスク チェーンで取り消しをサポートするときは、cancellation_token_source を使って cancellation_token を作成し、そのトークンを最初のタスクのコンストラクターに渡す方法をお勧めします。The preferred way to support cancellation in a task chain is to use a cancellation_token_source to create a cancellation_token, and then pass the token to the constructor of the initial task. キャンセル トークンを設定して非同期タスクを作成した場合、cancellation_token_source::cancel が呼び出されたときに、IAsync\* 操作に対する Cancel が自動的に呼び出され、取り消し要求が後続のタスク チェーンに渡されます。If an asynchronous task is created with a cancellation token, and cancellation_token_source::cancel is called, the task automatically calls Cancel on the IAsync\* operation and passes the cancellation request down its continuation chain. この基本的な方法を示す疑似コードを次に示します。The following pseudocode demonstrates the basic approach.

//Class member:
cancellation_token_source m_fileTaskTokenSource;

// Cancel button event handler:
m_fileTaskTokenSource.cancel();

// task chain
auto getFileTask2 = create_task(documentsFolder->GetFileAsync(fileName),
                                m_fileTaskTokenSource.get_token());
//getFileTask2.then ...

タスクが取り消されると、task_canceled 例外がタスク チェーンを通じて伝達されます。When a task is canceled, a task_canceled exception is propagated down the task chain. 値ベースの後続タスクは実行されないだけですが、タスクベースの後続タスクでは、task::get が呼び出されると例外がスローされます。Value-based continuations will simply not execute, but task-based continuations will cause the exception to be thrown when task::get is called. エラー処理を行う継続がある場合は、task_canceled 例外を明示的にキャッチするようにしてください If you have an error-handling continuation, make sure that it catches the task_canceled exception explicitly. (これは Platform::Exception から派生した例外ではありません)。(This exception is not derived from Platform::Exception.)

取り消しは連携して行います。Cancellation is cooperative. 継続で、UWP メソッドを呼び出すだけでなく、時間のかかる作業を行うときは、キャンセル トークンの状態を定期的に確認し、取り消された場合は実行を中止する必要があります。If your continuation does some long-running work beyond just invoking a UWP method, then it is your responsibility to check the state of the cancellation token periodically and stop execution if it is canceled. 継続で割り当てられたすべてのリソースをクリーンアップした後、cancel_current_task を呼び出してそのタスクを取り消し、以降の値ベースの継続に取り消しを伝達します。After you clean up all resources that were allocated in the continuation, call cancel_current_task to cancel that task and propagate the cancellation down to any value-based continuations that follow it. また、別の例として、FileSavePicker 操作の結果を表すタスク チェーンを作成するとします。Here's another example: you can create a task chain that represents the result of a FileSavePicker operation. ユーザーが [キャンセル] ボタンをクリックした場合、IAsyncInfo::Cancel メソッドは呼び出されません。If the user chooses the Cancel button, the IAsyncInfo::Cancel method is not called. 代わりに、操作は完了しますが nullptr が返されます。Instead, the operation succeeds but returns nullptr. この場合、継続で入力パラメーターをテストし、入力が nullptr の場合に cancel_current_task を呼び出すことができます。The continuation can test the input parameter and call cancel_current_task if the input is nullptr.

詳しくは、「PPL での取り消し」をご覧ください。For more information, see Cancellation in the PPL

タスク チェーンでのエラーの処理Handling errors in a task chain

先行タスクの取り消しや例外のスローが行われても継続を実行する場合は、継続のラムダ関数への入力を task または task (先行タスクのラムダが IAsyncAction^ を返す場合) として指定して、継続をタスクベースにします。If you want a continuation to execute even if the antecedent was canceled or threw an exception, then make the continuation a task-based continuation by specifying the input to its lambda function as a task or task if the lambda of the antecedent task returns an IAsyncAction^.

タスク チェーンでエラーや取り消しを処理するために、すべての後続タスクをタスクベースにしたり、スローする可能性があるすべての操作を try…catch ブロック内に含めたりする必要はありません。To handle errors and cancellation in a task chain, you don't have to make every continuation task-based or enclose every operation that might throw within a try…catch block. 代わりに、タスクベースの後続タスクをチェーンの最後に追加し、その後続タスクですべてのエラーを処理します。Instead, you can add a task-based continuation at the end of the chain and handle all errors there. 例外 ( task_canceled 例外も含む) はタスク チェーンを通じて伝達され、値ベースの継続はバイパスされるため、エラー処理を行うタスクベースの継続で例外を処理できます。Any exception—this includes a task_canceled exception—will propagate down the task chain and bypass any value-based continuations, so that you can handle it in the error-handling task-based continuation. エラー処理を行うタスクベースの後続タスクを使うように前の例を書き換えると次のようになります。We can rewrite the previous example to use an error-handling task-based continuation:

#include <ppltasks.h>
void App::DeleteWithTasksHandleErrors(String^ fileName)
{    
    using namespace Windows::Storage;
    using namespace concurrency;

    StorageFolder^ documentsFolder = KnownFolders::DocumentsLibrary;
    auto getFileTask = create_task(documentsFolder->GetFileAsync(fileName));

    getFileTask.then([](StorageFile^ storageFileSample)
    {       
        return storageFileSample->DeleteAsync();
    })

    .then([](task<void> t)
    {

        try
        {
            t.get();
            // .get() didn' t throw, so we succeeded.
            OutputDebugString(L"File deleted.");
        }
        catch (Platform::COMException^ e)
        {
            //Example output: The system cannot find the specified file.
            OutputDebugString(e->Message->Data());
        }

    });
}

タスクベースの後続タスクで、メンバー関数 task::get を呼び出してタスクの結果を取得しています。In a task-based continuation, we call the member function task::get to get the results of the task. task::get は、結果を生成しない IAsyncAction 操作の場合でも呼び出す必要があります。これは、task::get ではタスクに送られた例外も取得するためです。We still have to call task::get even if the operation was an IAsyncAction that produces no result because task::get also gets any exceptions that have been transported down to the task. 入力タスクに例外が格納されている場合、task::get の呼び出しでその例外がスローされます。If the input task is storing an exception, it is thrown at the call to task::get. task::get を呼び出していない場合、チェーンの最後でタスクベースの継続を使っていない場合、またはスローされた例外の種類をキャッチしていない場合は、タスクに対する参照がすべて削除されたときに unobserved_task_exception がスローされます。If you don't call task::get, or don't use a task-based continuation at the end of the chain, or don't catch the exception type that was thrown, then an unobserved_task_exception is thrown when all references to the task have been deleted.

キャッチする例外は処理できるものだけにしてください。Only catch the exceptions that you can handle. 回復できないエラーがアプリで発生した場合は、不明な状態のままアプリの実行を続けるのではなく、アプリをクラッシュさせることをお勧めします。If your app encounters an error that you can't recover from, it's better to let the app crash than to let it continue to run in an unknown state. また、一般に、unobserved_task_exception 自体はキャッチしないことをお勧めします。Also, in general, don't attempt to catch the unobserved_task_exception itself. この例外は、主に診断を目的としたものです。This exception is mainly intended for diagnostic purposes. unobserved_task_exception がスローされた場合、通常はコード内にバグがあることを示します。When unobserved_task_exception is thrown, it usually indicates a bug in the code. 原因のほとんどは、処理が必要な例外があること、またはコード内の他のエラーが原因で回復できない例外があることのどちらかです。Often the cause is either an exception that should be handled, or an unrecoverable exception that's caused by some other error in the code.

スレッド コンテキストの管理Managing the thread context

UWP アプリの UI は、シングルスレッド アパートメント (STA) で実行されます。The UI of a UWP app runs in a single-threaded apartment (STA). ラムダが IAsyncAction または IAsyncOperation を返すタスクは、アパートメントを認識します。A task whose lambda returns either an IAsyncAction or IAsyncOperation is apartment-aware. タスクが STA で作成されている場合、特に指定しない限り、その後続タスクもすべて STA で実行されます。If the task is created in the STA, then all of its continuations will run also run in it by default, unless you specify otherwise. つまり、タスク チェーン全体が親タスクからアパートメントの認識を継承します。In other words, the entire task chain inherits apartment-awareness from the parent task. この動作により、STA からしかアクセスできない UI コントロールの操作が簡単になります。This behavior helps simplify interactions with UI controls, which can only be accessed from the STA.

たとえば、UWP アプリの ListBox コントロールを設定するとき、XAML ページを表す任意のクラスのメンバー関数で task::then メソッド内から設定できるため、Dispatcher オブジェクトを使う必要がありません。For example, in a UWP app, in the member function of any class that represents a XAML page, you can populate a ListBox control from within a task::then method without having to use the Dispatcher object.

#include <ppltasks.h>
void App::SetFeedText()
{    
    using namespace Windows::Web::Syndication;
    using namespace concurrency;
    String^ url = "http://windowsteamblog.com/windows_phone/b/wmdev/atom.aspx";
    SyndicationClient^ client = ref new SyndicationClient();
    auto feedOp = client->RetrieveFeedAsync(ref new Uri(url));

    create_task(feedOp).then([this]  (SyndicationFeed^ feed)
    {
        m_TextBlock1->Text = feed->Title->Text;
    });
}

IAsyncAction または IAsyncOperation を返さないタスクは、アパートメントを認識しません。これらのタスクの後続タスクは、既定では、利用可能な最初のバックグラウンド スレッドで実行されます。If a task doesn't return an IAsyncAction or IAsyncOperation, then it's not apartment-aware and, by default, its continuations are run on the first available background thread.

既定のスレッド コンテキストは、どちらの種類のタスクについても、task_continuation_context を受け取る task::then のオーバーロードを使って上書きできます。You can override the default thread context for either kind of task by using the overload of task::then that takes a task_continuation_context. たとえば、状況によっては、アパートメントを認識するタスクの後続タスクをバックグラウンド スレッドでスケジュールする方が適している場合もあります。For example, in some cases, it might be desirable to schedule the continuation of an apartment-aware task on a background thread. このような場合は、task_continuation_context::use_arbitrary を渡して、マルチスレッド アパートメント内の次に利用可能なスレッドでタスクの処理をスケジュールできます。In such a case, you can pass task_continuation_context::use_arbitrary to schedule the task’s work on the next available thread in a multi-threaded apartment. これにより、継続の作業を UI スレッドで発生する他の作業と同期する必要がないため、継続のパフォーマンスが向上します。This can improve the performance of the continuation because its work doesn't have to be synchronized with other work that's happening on the UI thread.

次の例は、task_continuation_context::use_arbitrary オプションを指定すると役立つ状況の例を示しています。また、スレッド セーフでないコレクションの同時操作の同期に既定の継続のコンテキストがどのように役立つかも示しています。The following example demonstrates when it's useful to specify the task_continuation_context::use_arbitrary option, and it also shows how the default continuation context is useful for synchronizing concurrent operations on non-thread-safe collections. このコードでは、RSS フィードの URL の一覧をループ処理し、各 URL について、非同期操作を開始してフィード データを取得しています。In this code, we loop through a list of URLs for RSS feeds, and for each URL, we start up an async operation to retrieve the feed data. フィードを取得する順序は制御できませんが、ここでは問題ありません。We can’t control the order in which the feeds are retrieved, and we don't really care. RetrieveFeedAsync 操作が完了するたびに、1 つ目の継続が SyndicationFeed^ オブジェクトを受け取り、それを使ってアプリで定義されている FeedData^ オブジェクトを初期化します。When each RetrieveFeedAsync operation completes, the first continuation accepts the SyndicationFeed^ object and uses it to initialize an app-defined FeedData^ object. これらの操作はそれぞれ独立した操作であるため、継続のコンテキストとして task_continuation_context::use_arbitrary を指定すると処理が速くなる可能性があります。Because each of these operations is independent from the others, we can potentially speed things up by specifying the task_continuation_context::use_arbitrary continuation context. ただし、それぞれの FeedData オブジェクトを初期化した後に、そのオブジェクトを Vector に追加する必要があり、スレッド セーフなコレクションではありません。However, after each FeedData object is initialized, we have to add it to a Vector, which is not a thread-safe collection. そのため、ここでは、継続を作成して task_continuation_context::use_current を指定することで、Append の呼び出しがすべて同じアプリケーション シングルスレッド アパートメント (ASTA) コンテキストで実行されるようにしています。Therefore, we create a continuation and specify task_continuation_context::use_current to ensure that all the calls to Append occur in the same Application Single-Threaded Apartment (ASTA) context. task_continuation_context::use_default は既定のコンテキストであるため、明示的に指定する必要はありませんが、ここではわかりやすいように指定しています。Because task_continuation_context::use_default is the default context, we don’t have to specify it explicitly, but we do so here for the sake of clarity.

#include <ppltasks.h>
void App::InitDataSource(Vector<Object^>^ feedList, vector<wstring> urls)
{
                using namespace concurrency;
    SyndicationClient^ client = ref new SyndicationClient();

    std::for_each(std::begin(urls), std::end(urls), [=,this] (std::wstring url)
    {
        // Create the async operation. feedOp is an
        // IAsyncOperationWithProgress<SyndicationFeed^, RetrievalProgress>^
        // but we don't handle progress in this example.

        auto feedUri = ref new Uri(ref new String(url.c_str()));
        auto feedOp = client->RetrieveFeedAsync(feedUri);

        // Create the task object and pass it the async operation.
        // SyndicationFeed^ is the type of the return value
        // that the feedOp operation will eventually produce.

        // Then, initialize a FeedData object by using the feed info. Each
        // operation is independent and does not have to happen on the
        // UI thread. Therefore, we specify use_arbitrary.
        create_task(feedOp).then([this]  (SyndicationFeed^ feed) -> FeedData^
        {
            return GetFeedData(feed);
        }, task_continuation_context::use_arbitrary())

        // Append the initialized FeedData object to the list
        // that is the data source for the items collection.
        // This all has to happen on the same thread.
        // By using the use_default context, we can append
        // safely to the Vector without taking an explicit lock.
        .then([feedList] (FeedData^ fd)
        {
            feedList->Append(fd);
            OutputDebugString(fd->Title->Data());
        }, task_continuation_context::use_default())

        // The last continuation serves as an error handler. The
        // call to get() will surface any exceptions that were raised
        // at any point in the task chain.
        .then( [this] (task<void> t)
        {
            try
            {
                t.get();
            }
            catch(Platform::InvalidArgumentException^ e)
            {
                //TODO handle error.
                OutputDebugString(e->Message->Data());
            }
        }); //end task chain

    }); //end std::for_each
}

継続内で作成される入れ子になった新しいタスクは、最初のタスクのアパートメントの認識を継承しません。Nested tasks, which are new tasks that are created inside a continuation, don't inherit apartment-awareness of the initial task.

進行状況の更新の処理Handing progress updates

IAsyncOperationWithProgress または IAsyncActionWithProgress をサポートするメソッドは、実行中の操作が完了するまでの間、定期的に進行状況の更新を提供します。Methods that support IAsyncOperationWithProgress or IAsyncActionWithProgress provide progress updates periodically while the operation is in progress, before it completes. この進行状況の報告は、タスクや継続とは別に独立して処理されます。Progress reporting is independent from the notion of tasks and continuations. オブジェクトの Progress プロパティのデリゲートを指定するだけでかまいません。You just supply the delegate for the object’s Progress property. このデリゲートの一般的な用途は、UI の進行状況バーを更新することです。A typical use of the delegate is to update a progress bar in the UI.