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.

この記事では、Visual C コンポーネント拡張機能での非同期メソッドを使用することをお勧めの方法を説明します。 (C +/cli CX) を使用して、taskクラスで定義されている、 concurrency ppltasks.h 内の名前空間。This article describes the recommended way to consume asynchronous methods in Visual C++ 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 クラス and its related types and functions, which are contained in the 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. 詳細については、に関するドキュメントを完成タスクおよびその関連メソッドを含む作成_タスク, see Task Parallelism (Concurrency Runtime)します。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 =

    // 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 メソッド、直感的で簡単な方法でタスクのチェーンを作成することができますメソッドを返します、タスク 場所。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 つのタスク チェーンを構成することができます。myTask.then(…).then(…).then(…);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.

  を使用する方法の 1 つは、タスクのチェーンを作成、タスク非同期操作を構成するクラス。Note  Creating 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 の戻り値の型.then return type
TResultTResult タスクtask
IAsyncOperation^IAsyncOperation^ タスクtask
IAsyncOperationWithProgress<TResult, TProgress>^IAsyncOperationWithProgress<TResult, TProgress>^ タスクtask
IAsyncAction^IAsyncAction^ タスクtask
IAsyncActionWithProgress^IAsyncActionWithProgress^ タスクtask
タスクtask タスクtask

タスクの取り消し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戻り型をキャンセル method that it inherits from IAsyncInfoには不適切ですメソッドの外部に公開します。Although each *Async return type has a Cancel method that it inherits from IAsyncInfo, it's awkward to expose it to outside methods. タスク チェーンでキャンセルをサポートするために推奨される方法は使用する、 キャンセル_トークン_ソースを作成する、 キャンセル_トークン、最初のタスクのコンス トラクターにトークンを渡します。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. キャンセル トークンを使用する非同期タスクを作成した場合とキャンセル_トークン_source::cancel が呼び出されると、タスクに自動的に呼び出すキャンセル上、 IAsync* その継続のチェーンをキャンセルが要求操作を渡します。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:

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

タスクが取り消されたときに、 タスク_キャンセル exception is propagated down the task chain. Value-based continuations will simply not execute, but task-based continuations will cause the exception to be thrown when task::getが呼び出されます。When a task is canceled, a task_canceled exception is propagated down the task chain. Value-based continuations will simply not execute, but task-based continuations will cause the exception to be thrown when task::get is called. エラー処理の継続がある場合は場合、キャッチすることを確認、タスク_キャンセル例外明示的にします。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. 継続タスクに割り当てられたすべてのリソースをクリーンアップする後キャンセル_現在_タスクそのタスクをキャンセルし、いずれかにキャンセルを伝達するにはそれに続く値ベースの継続します。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します。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. 例外: これが含まれています、 タスク_キャンセル例外: チェーン内のタスクに反映され、エラー処理にそれを処理できるように、すべての値ベースの継続をバイパスタスク ベースの継続します。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)

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


メンバー関数は、タスク ベースの継続で呼び出して 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、スローされた例外の種類の例外をキャッチしないまたはチェーンの最後に、タスク ベースの継続を使用しないでください、無視された_タスク_例外タスクにすべての参照が削除された場合にスローされます。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. また、一般に、しないでキャッチ、無視された_タスク_例外自体。Also, in general, don't attempt to catch the unobserved_task_exception itself. この例外は、主に診断を目的としたものです。This exception is mainly intended for diagnostic purposes. ときに無視された_タスク_例外がスローされると、通常を示します、コードのバグです。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 or 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.

たとえば、XAML ページを表すクラスのメンバー関数での UWP アプリで設定できます、 ListBox 内からコントロールを task::thenメソッドを使用しなくても、 ディスパッチャー オブジェクト。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 or 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::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. このような場合は、渡すことができますタスク_継続_context::use_任意で [次へ] の使用可能なスレッドでタスクの作業をスケジュールする、マルチ スレッド アパートメント。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.

次の例で指定すると便利な場合、 タスク_継続_context::use_任意オプション、およびそれも表示する方法既定の継続コンテキストはスレッド セーフのコレクションでの同時操作を同期するために便利です。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. これらの各操作は、他のユーザーから独立しているためできる場合もあります速度を向上させるを指定することによって、タスク_継続_context::use_任意継続コンテキスト.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. そのため、継続を作成し、指定タスク_継続_context::use_現在ことに、すべての呼び出しを確認するAppend 同じ Application Single-Threaded アパートメント (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. タスク_継続_context::use_既定既定のコンテキストには、それを明示的に指定する必要はありませんがこちらから行うことの sake のわかりやすくするためです。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)
        }, 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)
            catch(Platform::InvalidArgumentException^ e)
                //TODO handle error.
        }); //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.