Programação assíncrona em C++/CXAsynchronous programming in C++/CX

Observação

Este tópico existe para ajudar você na manutenção do seu aplicativo C++/CX.This topic exists to help you maintain your C++/CX application. Recomendamos que você use C++/WinRT para novos aplicativos.But we recommend that you use C++/WinRT for new applications. C++/WinRT é uma projeção de linguagem C++17 completamente moderna e padrão para APIs do WinRT (Windows Runtime), implementada como uma biblioteca com base em cabeçalho e arquivo, projetada para fornecer acesso de primeira classe à API moderna do Windows.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.

Este artigo descreve a maneira recomendada para consumir métodos assíncronos em extensões de componente do Visual C++ (C++/CX) usando a classe task definida no namespace concurrency em 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.

Tipos assíncronos da Plataforma Universal do Windows (UWP)Universal Windows Platform (UWP) asynchronous types

A Plataforma Universal do Windows (UWP) apresenta um modelo bem definido para chamar métodos assíncronos e fornece os tipos necessários para consumir esses métodos.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. Se você não estiver familiarizado com o modelo assíncrono UWP, leia Programação Assíncrona antes de continuar a ler este artigo.If you are not familiar with the UWP asynchronous model, read Asynchronous Programming before you read the rest of this article.

Embora você possa consumir as APIs de Windows Runtime assíncronas diretamente em C++, a abordagem preferida é usar a classe Task e seus tipos e funções relacionados, que estão contidas no namespace de simultaneidade e definidas em <ppltasks.h> .Although you can consume the asynchronous Windows Runtime 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>. A concurrency::task é um tipo de finalidade geral, mas quando é usado o switch do compilador /ZW, que é obrigatório para aplicativos e componentes Plataforma Universal do Windows (UWP), a classe task encapsula os tipos assíncronos UWP para que seja mais fácil: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:

  • encadear diversas operações assíncronas e síncronaschain multiple asynchronous and synchronous operations together

  • lidar com exceções em cadeias de tarefashandle exceptions in task chains

  • executar cancelamentos em cadeias de tarefasperform cancellation in task chains

  • assegurar que as tarefas individuais executem no apartment ou contexto do thread apropriadoensure that individual tasks run in the appropriate thread context or apartment

Este artigo fornece orientação básica sobre como usar a classe task com as APIS assíncronas UWP.This article provides basic guidance about how to use the task class with the UWP asynchronous APIs. Para obter uma documentação mais completa sobre a tarefa e seus métodos relacionados, incluindo criar _ tarefa, consulte paralelismo de tarefas (tempo de execução de simultaneidade).For more complete documentation about task and its related methods including create_task, see Task Parallelism (Concurrency Runtime).

Consumindo uma operação assíncrona usando uma tarefaConsuming an async operation by using a task

O exemplo a seguir mostra como usar a classe task para consumir um método async que retorna uma interface IAsyncOperation e cuja operação produz um valor.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. Veja a seguir as etapas básicas:Here are the basic steps:

  1. Chame o método create_task e passe o objeto IAsyncOperation^.Call the create_task method and pass it the IAsyncOperation^ object.

  2. Chame a função de membro task::then na tarefa e forneça um lambda que será invocado quando a operação assíncrona for concluída.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...
}

A tarefa criada e retornada pela função task::then é conhecida como continuação.The task that's created and returned by the task::then function is known as a continuation. O argumento de entrada (neste caso) para o lambda fornecido pelo usuário é o resultado que a operação da tarefa produz quando concluída.The input argument (in this case) to the user-provided lambda is the result that the task operation produces when it completes. É o mesmo valor que seria recuperado chamando IAsyncOperation::GetResults se você estivesse usando diretamente a interface IAsyncOperation.It's the same value that would be retrieved by calling IAsyncOperation::GetResults if you were using the IAsyncOperation interface directly.

O método task::then retorna imediatamente, e seu delegado não é executado enquanto o trabalho assíncrono não for concluído com êxito.The task::then method returns immediately, and its delegate doesn't run until the asynchronous work completes successfully. Neste exemplo, se a operação assíncrona causar o lançamento de uma exceção ou terminar no estado cancelado como resultado de uma solicitação de cancelamento, a continuação jamais será executada.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. Posteriormente, descreveremos como gravar continuações que são executadas mesmo quando a tarefa anterior é cancelada ou apresenta falha.Later, we’ll describe how to write continuations that execute even if the previous task was cancelled or failed.

Apesar de você declarar a variável da tarefa na pilha local, ela gerencia seu tempo de vida para que não seja excluída até que todas as suas operações sejam concluídas e todas as referências a ela saiam do escopo, mesmo que o método seja retornado antes da conclusão das operações.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.

Criando uma cadeia de tarefasCreating a chain of tasks

Na programação assíncrona, é comum definir uma sequência de operações, também conhecida como cadeias de tarefas, nas quais cada continuação é executada somente quando a anterior for concluída.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. Em alguns casos, a tarefa anterior (ou antecedente) produz um valor que a continuação aceita como entrada.In some cases, the previous (or antecedent) task produces a value that the continuation accepts as input. Usando o método task::then, você pode criar cadeias de tarefas de maneira intuitiva e simples; o método retorna uma task onde T é o tipo de retorno da função lambda.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. Você pode compor várias continuações em uma cadeia de tarefas: myTask.then(…).then(…).then(…);You can compose multiple continuations into a task chain: myTask.then(…).then(…).then(…);

Cadeias de tarefas são especialmente úteis quando uma continuação cria uma nova operação assíncrona. Esse tipo de tarefa é conhecida como uma tarefa assíncrona.Task chains are especially useful when a continuation creates a new asynchronous operation; such a task is known as an asynchronous task. O exemplo a seguir ilustra uma cadeia de tarefas com duas continuações.The following example illustrates a task chain that has two continuations. A tarefa inicial adquire o identificador para um arquivo existente e, quando essa operação é concluída, a primeira continuação inicia uma nova operação assíncrona para excluir o arquivo.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. Quando essa operação é concluída, a segunda continuação é executada e gera uma mensagem de confirmação.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.");
    });
}

O exemplo anterior ilustra quatro pontos importantes:The previous example illustrates four important points:

  • A primeira continuação converte o objeto IAsyncAction^ em uma tarefa e retorna a tarefa.The first continuation converts the IAsyncAction^ object to a task and returns the task.

  • A segunda continuação não trata erros e, portanto, aceita void e não tarefa como entrada.The second continuation performs no error handling, and therefore takes void and not task as input. Ela é uma continuação baseada em valores.It is a value-based continuation.

  • A segunda continuação não é executada até que a operação DeleteAsync seja concluída.The second continuation doesn't execute until the DeleteAsync operation completes.

  • Como a segunda continuação é baseada em valores, se a operação que foi iniciada pela chamada a DeleteAsync lançar uma exceção, a segunda continuação não será executada.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.

Observação    Criar uma cadeia de tarefas é apenas uma das maneiras de usar a classe Task para compor operações assíncronas.Note  Creating a task chain is just one of the ways to use the task class to compose asynchronous operations. Você também pode compor operações usando operadores de junção e escolha && e || .You can also compose operations by using join and choice operators && and ||. Para obter mais informações, consulte Paralelismo de tarefas (Tempo de Execução de Simultaneidade).For more information, see Task Parallelism (Concurrency Runtime).

Tipos de retorno da função lambda e tipos de retorno de tarefasLambda function return types and task return types

Em uma continuação de tarefa, o tipo de retorno da função lambda é encapsulado em um objeto task.In a task continuation, the return type of the lambda function is wrapped in a task object. Se lambda retornar um double, então, o tipo de tarefa de continuação será task.If the lambda returns a double, then the type of the continuation task is task. No entanto, o objeto task foi desenvolvido para que não produza tipos de retorno desnecessariamente aninhados.However, the task object is designed so that it doesn't produce needlessly nested return types. Se uma lambda retorna um IAsyncOperation<SyndicationFeed^>^, a continuação retorna um task<SyndicationFeed^> e não um task<task<SyndicationFeed^>> ou task<IAsyncOperation<SyndicationFeed^>^>^.If a lambda returns an IAsyncOperation<SyndicationFeed^>^, the continuation returns a task<SyndicationFeed^>, not a task<task<SyndicationFeed^>> or task<IAsyncOperation<SyndicationFeed^>^>^. Esse processo é conhecido como não encapsulamento assíncrono e assegura que a operação assíncrona dentro da continuação seja concluída antes que a próxima continuação seja invocada.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.

No exemplo anterior, observe que a tarefa retorna uma tarefa apesar de seu lambda ter retornado um objeto IAsyncInfo.In the previous example, notice that the task returns a task even though its lambda returned an IAsyncInfo object. A tabela a seguir resume as conversões de tipo que ocorrem entre uma função lambda e a tarefa que a delimita:The following table summarizes the type conversions that occur between a lambda function and the enclosing task:

tipo de retorno de lambdalambda return type .then tipo de retorno.then return type
TResultTResult agendatask
IAsyncOperation^IAsyncOperation^ agendatask
IAsyncOperationWithProgress<TResult, TProgress>^IAsyncOperationWithProgress<TResult, TProgress>^ agendatask
IAsyncAction^IAsyncAction^ agendatask
IAsyncActionWithProgress^IAsyncActionWithProgress^ agendatask
agendatask agendatask

Cancelando tarefasCanceling tasks

É uma boa ideia dar ao usuário a opção de cancelar uma operação assíncrona.It is often a good idea to give the user the option to cancel an asynchronous operation. Em alguns casos, talvez seja necessário cancelar uma operação programaticamente de fora da cadeia da tarefa.And in some cases you might have to cancel an operation programmatically from outside the task chain. Embora cada * tipo de retorno assíncrono tenha um método Cancel que ele herde de IAsyncInfo, é estranho expô-lo a métodos externos.Although each *Async return type has a Cancel method that it inherits from IAsyncInfo, it's awkward to expose it to outside methods. A maneira preferida de dar suporte ao cancelamento em uma cadeia de tarefas é usar uma ** _ _ origem de token de cancelamento** para criar um ** _ token de cancelamento**e, em seguida, passar o token para o construtor da tarefa inicial.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. Se uma tarefa assíncrona for criada com um token de cancelamento e a origem do token de cancelamento _ _ :: Cancel for chamada, a tarefa chamará o cancelamento automaticamente na operação **IAsync * ** e passará a solicitação de cancelamento para baixo em sua cadeia de continuação.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. O pseudocódigo a seguir demonstra a abordagem básica.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 ...

Quando uma tarefa é cancelada, uma exceção de tarefa _ cancelada é propagada para baixo na cadeia de tarefas.When a task is canceled, a task_canceled exception is propagated down the task chain. As continuações baseadas em valores simplesmente não executam, mas as continuações baseadas em tarefas fazem com que a exceção seja lançada quando task::get é chamado.Value-based continuations will simply not execute, but task-based continuations will cause the exception to be thrown when task::get is called. Se você tiver uma continuação de tratamento de erros, verifique se ela captura a exceção de tarefa _ cancelada explicitamente.If you have an error-handling continuation, make sure that it catches the task_canceled exception explicitly. (Essa exceção não é derivada de Platform::Exception.)(This exception is not derived from Platform::Exception.)

O cancelamento é cooperativo.Cancellation is cooperative. Se a continuação realiza algum trabalho de execução demorada além de apenas invocar um método UWP, então, é sua responsabilidade verificar o estado do token de cancelamento periodicamente e parar a execução caso ele seja cancelado.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. Depois de limpar todos os recursos que foram alocados na continuação, chame Cancelar _ _ tarefa atual para cancelar essa tarefa e propagar o cancelamento para qualquer continuação baseada em valor que o siga.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. Veja outro exemplo: você pode criar uma cadeia de tarefa que representa o resultado de uma operação FileSavePicker.Here's another example: you can create a task chain that represents the result of a FileSavePicker operation. Se o usuário escolhe o botão Cancel, o método IAsyncInfo::Cancel não é chamado.If the user chooses the Cancel button, the IAsyncInfo::Cancel method is not called. Em vez disso, a operação tem êxito mas retorna nullptr.Instead, the operation succeeds but returns nullptr. A continuação pode testar o parâmetro de entrada e chamar _ Cancelar _ tarefa atual se a entrada for nullptr.The continuation can test the input parameter and call cancel_current_task if the input is nullptr.

Para obter mais informações, consulte Cancellation in the PPLFor more information, see Cancellation in the PPL

Manipulando erros em uma cadeia de tarefaHandling errors in a task chain

Se você deseja que uma continuação seja executada mesmo que a antecedente tenha sido cancelada ou tenha lançado uma exceção, torne-a uma continuação baseada em tarefa especificando a entrada de sua função lambda como uma tarefa ou tarefa se a lambda da tarefa antecedente retorna uma 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^.

Para manipular erros e cancelamentos em uma cadeia de tarefa, não é necessário transformar cada baseada em tarefa ou encapsular toda operação que possa lançar em um bloco 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. Em vez disso, adicione uma continuação baseada em tarefa no fim da cadeia e manipule todos os erros nela.Instead, you can add a task-based continuation at the end of the chain and handle all errors there. Qualquer exceção — isso inclui uma exceção de tarefa _ cancelada – propagará a cadeia de tarefas e ignorará qualquer continuação baseada em valor, para que você possa tratá-la na continuação baseada em tarefa de tratamento de erros.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. Podemos recriar o exemplo anterior para usar uma continuação baseada em tarefas de tratamento de erro: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());
        }

    });
}

Em uma continuação baseada em tarefa, a função membro task::get é chamada para obter os resultados da tarefa.In a task-based continuation, we call the member function task::get to get the results of the task. Ainda é obrigatório chamar task::get mesmo que a operação seja uma IAsyncAction que não produz resultados porque task::get também obtém todas as exceções que foram transportadas adiante para a tarefa.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. Se a tarefa de entrada armazena uma exceção, ela será lançada na chamada de task::get.If the input task is storing an exception, it is thrown at the call to task::get. Se você não chamar a tarefa:: Getou não usar uma continuação baseada em tarefa no final da cadeia, ou não capturar o tipo de exceção que foi lançado, uma exceção de ** _ tarefa _ não observada** será lançada quando todas as referências à tarefa tiverem sido excluídas.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.

Capture somente as exceções que você pode manipular.Only catch the exceptions that you can handle. Se o aplicativo encontrar um erro do qual não possa se recuperar, será melhor deixar que o aplicativo falhe em vez de deixar que ele continue a ser executado em um estado desconhecido.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. Além disso, em geral, não tente capturar a própria exceção de _ tarefa _ não observada .Also, in general, don't attempt to catch the unobserved_task_exception itself. A finalidade principal dessa exceção é diagnóstico.This exception is mainly intended for diagnostic purposes. Quando a exceção de _ tarefa _ não observada é lançada, ela geralmente indica um bug no código.When unobserved_task_exception is thrown, it usually indicates a bug in the code. A causa costuma ser uma exceção que precisa ser manipulada ou uma exceção irrecuperável causada por outro erro no código.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.

Gerenciando o contexto do threadManaging the thread context

A interface do usuário de um app UWP é executada em um STA.The UI of a UWP app runs in a single-threaded apartment (STA). Uma tarefa cujo lambda retornar IAsyncAction ou IAsyncOperation reconhece o apartment.A task whose lambda returns either an IAsyncAction or IAsyncOperation is apartment-aware. Se a tarefa for criada no STA, todas as suas continuações também serão executadas nele por padrão, a menos que você especifique de outra forma.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. Em outras palavras, a cadeia de tarefas inteira herda o reconhecimento de apartment da tarefa pai.In other words, the entire task chain inherits apartment-awareness from the parent task. Esse comportamento ajuda a simplificar as interações com os controles da interface do usuário, que podem ser acessados somente pelo STA.This behavior helps simplify interactions with UI controls, which can only be accessed from the STA.

Por exemplo, em um app UWP, na função membro de qualquer classe que representa uma página XAML, você pode preencher um controle ListBox de dentro de um método task::then sem ter que usar o objeto 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;
    });
}

Se uma tarefa não retorna uma IAsyncAction ou IAsyncOperation, ela não reconhece o apartment e, por padrão, suas continuações são executadas no primeiro thread em segundo plano disponível.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.

Você pode substituir o contexto de thread padrão por qualquer tipo de tarefa usando a sobrecarga de Task::, que usa um ** _ _ contexto de continuação de tarefa**.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. Por exemplo, em alguns casos, pode ser desejável agendar a continuação de uma tarefa que reconhece o apartment em um thread de segundo plano.For example, in some cases, it might be desirable to schedule the continuation of an apartment-aware task on a background thread. Nesse caso, você pode passar o contexto de continuação da tarefa _ _ :: use _ arbitrário para agendar o trabalho da tarefa no próximo thread disponível em um apartamento multi-threaded.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. Isso pode melhorar o desempenho da continuação, pois a sua execução não precisa ser sincronizada com as demais execuções realizadas no thread da interface do usuário.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.

O exemplo a seguir demonstra quando é útil especificar o **contexto de continuação da tarefa _ _ : use _ ** a opção arbitrária e também mostra como o contexto de continuação padrão é útil para sincronizar operações simultâneas em coleções sem segurança de thread.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. Nesse código, executamos um loop por uma lista de URLs para RSS Feeds e, para cada URL, iniciamos uma operação assíncrona para recuperar os dados de feed.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. Não é possível controlar a ordem na qual os feeds são recuperados, mas isso não é relevante.We can’t control the order in which the feeds are retrieved, and we don't really care. Quando cada operação RetrieveFeedAsync é concluída, a primeira continuação aceita o objeto SyndicationFeed^ e o usa para inicializar um objeto FeedData^ definido pelo aplicativo.When each RetrieveFeedAsync operation completes, the first continuation accepts the SyndicationFeed^ object and uses it to initialize an app-defined FeedData^ object. Como cada uma dessas operações é independente das outras, podemos, potencialmente, acelerar as coisas especificando o contexto de continuação da tarefa _ _ :: _ usar o contexto de continuação arbitrária.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. No entanto, depois que cada objeto FeedData é inicializado, é necessário adicioná-lo a um Vector, que não é uma coleção não segura de threads.However, after each FeedData object is initialized, we have to add it to a Vector, which is not a thread-safe collection. Portanto, criamos uma continuação e especificamos o contexto de continuação da tarefa _ _ :: use _ Current para garantir que todas as chamadas a Append ocorram no mesmo contexto de aplicativo single-threaded apartment (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. Como o contexto de continuação da tarefa _ _ :: usar o _ padrão é o contexto padrão, não precisamos especificá-lo explicitamente, mas fazemos isso aqui para fins de clareza.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
}

Tarefas aninhadas, que são novas tarefas criadas dentro de uma continuação, não herdam o reconhecimento de apartment da tarefa inicial.Nested tasks, which are new tasks that are created inside a continuation, don't inherit apartment-awareness of the initial task.

Manipulando atualizações de progressoHanding progress updates

Os métodos que dão suporte a IAsyncOperationWithProgress ou IAsyncActionWithProgress fornecem atualizações de progresso periodicamente enquanto a operação está em andamento, antes de ser concluída.Methods that support IAsyncOperationWithProgress or IAsyncActionWithProgress provide progress updates periodically while the operation is in progress, before it completes. O relatório do progresso é independente da noção de tarefas e continuações.Progress reporting is independent from the notion of tasks and continuations. Basta fornecer o delegado da propriedade Progress do objeto.You just supply the delegate for the object’s Progress property. Um uso comum do delegado é para atualizar uma barra de progresso na interface do usuário.A typical use of the delegate is to update a progress bar in the UI.