Paralelismo de tarefa (runtime de simultaneidade)Task Parallelism (Concurrency Runtime)

Na Tempo de Execução de Simultaneidade, uma tarefa é uma unidade de trabalho que executa um trabalho específico e normalmente é executada em paralelo com outras tarefas.In the Concurrency Runtime, a task is a unit of work that performs a specific job and typically runs in parallel with other tasks. Uma tarefa pode ser decomposta em tarefas adicionais e mais refinadas que são organizadas em um grupo de tarefas.A task can be decomposed into additional, more fine-grained tasks that are organized into a task group.

Você usa tarefas quando escreve código assíncrono e deseja que alguma operação ocorra após a conclusão da operação assíncrona.You use tasks when you write asynchronous code and want some operation to occur after the asynchronous operation completes. Por exemplo, você pode usar uma tarefa para ler de forma assíncrona de um arquivo e, em seguida, usar outra tarefa — uma tarefa de continuação, que é explicada mais adiante neste documento — para processar os dados depois que ele se tornar disponível.For example, you could use a task to asynchronously read from a file and then use another task—a continuation task, which is explained later in this document—to process the data after it becomes available. Por outro lado, você pode usar grupos de tarefas para decompor trabalho paralelo em partes menores.Conversely, you can use tasks groups to decompose parallel work into smaller pieces. Por exemplo, suponha que você tenha um algoritmo recursivo que divide o trabalho restante em duas partições.For example, suppose you have a recursive algorithm that divides the remaining work into two partitions. Você pode usar grupos de tarefas para executar essas partições simultaneamente e aguardar a conclusão do trabalho dividido.You can use task groups to run these partitions concurrently, and then wait for the divided work to complete.

Dica

Quando você deseja aplicar a mesma rotina a todos os elementos de uma coleção em paralelo, use um algoritmo paralelo, como simultaneidade::p arallel_for, em vez de uma tarefa ou grupo de tarefas.When you want to apply the same routine to every element of a collection in parallel, use a parallel algorithm, such as concurrency::parallel_for, instead of a task or task group. Para obter mais informações sobre algoritmos paralelos, consulte algoritmos paralelos.For more information about parallel algorithms, see Parallel Algorithms.

Pontos PrincipaisKey Points

  • Ao passar variáveis para uma expressão lambda por referência, você deve garantir que o tempo de vida dessa variável persista até que a tarefa seja concluída.When you pass variables to a lambda expression by reference, you must guarantee that the lifetime of that variable persists until the task finishes.

  • Use tarefas (a classe Concurrency:: Task ) ao escrever código assíncrono.Use tasks (the concurrency::task class) when you write asynchronous code. A classe Task usa o ThreadPool do Windows como seu Agendador, não o Tempo de Execução de Simultaneidade.The task class uses the Windows ThreadPool as its scheduler, not the Concurrency Runtime.

  • Use grupos de tarefas (a classe Concurrency:: task_group ou o algoritmo concurrency::p arallel_invoke ) quando desejar decompor um trabalho paralelo em partes menores e, em seguida, esperar que essas partes menores sejam concluídas.Use task groups (the concurrency::task_group class or the concurrency::parallel_invoke algorithm) when you want to decompose parallel work into smaller pieces and then wait for those smaller pieces to complete.

  • Use o método Concurrency:: Task:: then para criar continuações.Use the concurrency::task::then method to create continuations. Uma continuação é uma tarefa executada de forma assíncrona após a conclusão de outra tarefa.A continuation is a task that runs asynchronously after another task completes. Você pode conectar qualquer número de continuações para formar uma cadeia de trabalho assíncrono.You can connect any number of continuations to form a chain of asynchronous work.

  • Uma continuação baseada em tarefas é sempre agendada para execução quando a tarefa antecedente é concluída, mesmo quando a tarefa Antecedent é cancelada ou gera uma exceção.A task-based continuation is always scheduled for execution when the antecedent task finishes, even when the antecedent task is canceled or throws an exception.

  • Use Concurrency:: when_all para criar uma tarefa que é concluída após a conclusão de cada membro de um conjunto de tarefas.Use concurrency::when_all to create a task that completes after every member of a set of tasks completes. Use Concurrency:: when_any para criar uma tarefa que é concluída após a conclusão de um membro de um conjunto de tarefas.Use concurrency::when_any to create a task that completes after one member of a set of tasks completes.

  • Tarefas e grupos de tarefas podem participar do mecanismo de cancelamento da PPL (biblioteca de padrões paralelos).Tasks and task groups can participate in the Parallel Patterns Library (PPL) cancellation mechanism. Para obter mais informações, consulte cancelamento na ppl.For more information, see Cancellation in the PPL.

  • Para saber como o tempo de execução manipula exceções que são geradas por tarefas e grupos de tarefas, consulte manipulação de exceção.To learn how the runtime handles exceptions that are thrown by tasks and task groups, see Exception Handling.

Neste DocumentoIn this Document

Usando expressões lambdaUsing Lambda Expressions

Devido à sua sintaxe sucinta, as expressões lambda são uma maneira comum de definir o trabalho que é executado por tarefas e grupos de tarefas.Because of their succinct syntax, lambda expressions are a common way to define the work that is performed by tasks and task groups. Aqui estão algumas dicas de uso:Here are some usage tips:

  • Como as tarefas normalmente são executadas em threads em segundo plano, lembre-se do tempo de vida do objeto quando você captura variáveis em expressões lambda.Because tasks typically run on background threads, be aware of the object lifetime when you capture variables in lambda expressions. Quando você captura uma variável por valor, uma cópia dessa variável é feita no corpo lambda.When you capture a variable by value, a copy of that variable is made in the lambda body. Quando você captura por referência, uma cópia não é feita.When you capture by reference, a copy is not made. Portanto, certifique-se de que o tempo de vida de qualquer variável capturada por referência providará a tarefa que a utiliza.Therefore, ensure that the lifetime of any variable that you capture by reference outlives the task that uses it.

  • Quando você passa uma expressão lambda para uma tarefa, não Capture variáveis que são alocadas na pilha por referência.When you pass a lambda expression to a task, don't capture variables that are allocated on the stack by reference.

  • Seja explícito sobre as variáveis capturadas em expressões lambda para que você possa identificar o que está capturando por valor versus referência.Be explicit about the variables you capture in lambda expressions so that you can identify what you're capturing by value versus by reference. Por esse motivo, recomendamos que você não use as [=] [&] Opções ou para expressões lambda.For this reason we recommend that you do not use the [=] or [&] options for lambda expressions.

Um padrão comum é quando uma tarefa em uma cadeia de continuação é atribuída a uma variável, e outra tarefa lê essa variável.A common pattern is when one task in a continuation chain assigns to a variable, and another task reads that variable. Não é possível capturar por valor porque cada tarefa de continuação manteria uma cópia diferente da variável.You can't capture by value because each continuation task would hold a different copy of the variable. Para variáveis alocadas para pilha, você também não pode capturar por referência porque a variável pode não ser mais válida.For stack-allocated variables, you also can't capture by reference because the variable may no longer be valid.

Para resolver esse problema, use um ponteiro inteligente, como std:: shared_ptr, para encapsular a variável e passar o ponteiro inteligente por valor.To solve this problem, use a smart pointer, such as std::shared_ptr, to wrap the variable and pass the smart pointer by value. Dessa forma, o objeto subjacente pode ser atribuído e lido e sobreviver além as tarefas que o utilizam.In this way, the underlying object can be assigned to and read from, and will outlive the tasks that use it. Use essa técnica mesmo quando a variável for um ponteiro ou um identificador contado por referência ( ^ ) para um objeto Windows Runtime.Use this technique even when the variable is a pointer or a reference-counted handle (^) to a Windows Runtime object. Este é um exemplo básico:Here's a basic example:

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

using namespace concurrency;
using namespace std;

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

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

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

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

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

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

Para obter mais informações sobre expressões lambda, consulte expressões lambda.For more information about lambda expressions, see Lambda Expressions.

A classe TaskThe task Class

Você pode usar a classe Concurrency:: Task para compor tarefas em um conjunto de operações dependentes.You can use the concurrency::task class to compose tasks into a set of dependent operations. Esse modelo de composição tem suporte pela noção de continuações.This composition model is supported by the notion of continuations. Uma continuação permite que o código seja executado quando a tarefa anterior, ou antecedente, for concluída.A continuation enables code to be executed when the previous, or antecedent, task completes. O resultado da tarefa Antecedent é passado como a entrada para uma ou mais tarefas de continuação.The result of the antecedent task is passed as the input to the one or more continuation tasks. Quando uma tarefa Antecedent é concluída, todas as tarefas de continuação que estão aguardando nela são agendadas para execução.When an antecedent task completes, any continuation tasks that are waiting on it are scheduled for execution. Cada tarefa de continuação recebe uma cópia do resultado da tarefa Antecedent.Each continuation task receives a copy of the result of the antecedent task. Por sua vez, essas tarefas de continuação também podem ser tarefas antecedentes para outras continuações, criando assim uma cadeia de tarefas.In turn, those continuation tasks may also be antecedent tasks for other continuations, thereby creating a chain of tasks. As continuações ajudam a criar cadeias de comprimento arbitrário de tarefas que têm dependências específicas entre elas.Continuations help you create arbitrary-length chains of tasks that have specific dependencies among them. Além disso, uma tarefa pode participar do cancelamento antes que as tarefas sejam iniciadas ou de maneira cooperativa enquanto estiverem em execução.In addition, a task can participate in cancellation either before a tasks starts or in a cooperative manner while it is running. Para obter mais informações sobre esse modelo de cancelamento, consulte cancelamento na ppl.For more information about this cancellation model, see Cancellation in the PPL.

taské uma classe de modelo.task is a template class. O parâmetro de tipo T é o tipo do resultado que é produzido pela tarefa.The type parameter T is the type of the result that is produced by the task. Esse tipo pode ser void se a tarefa não retornar um valor.This type can be void if the task does not return a value. TNão é possível usar o const modificador.T cannot use the const modifier.

Ao criar uma tarefa, você fornece uma função de trabalho que executa o corpo da tarefa.When you create a task, you provide a work function that performs the task body. Essa função de trabalho vem na forma de uma função lambda, um ponteiro de função ou um objeto de função.This work function comes in the form of a lambda function, function pointer, or function object. Para aguardar a conclusão de uma tarefa sem obter o resultado, chame o método Concurrency:: Task:: Wait .To wait for a task to finish without obtaining the result, call the concurrency::task::wait method. O task::wait método retorna um valor Concurrency:: task_status que descreve se a tarefa foi concluída ou cancelada.The task::wait method returns a concurrency::task_status value that describes whether the task was completed or canceled. Para obter o resultado da tarefa, chame o método Concurrency:: Task:: Get .To get the result of the task, call the concurrency::task::get method. Esse método chama task::wait para aguardar a conclusão da tarefa e, portanto, bloqueia a execução do thread atual até que o resultado esteja disponível.This method calls task::wait to wait for the task to finish, and therefore blocks execution of the current thread until the result is available.

O exemplo a seguir mostra como criar uma tarefa, aguardar seu resultado e exibir seu valor.The following example shows how to create a task, wait for its result, and display its value. Os exemplos nesta documentação usam funções lambda porque fornecem uma sintaxe mais sucinta.The examples in this documentation use lambda functions because they provide a more succinct syntax. No entanto, você também pode usar ponteiros de função e objetos de função ao usar tarefas.However, you can also use function pointers and function objects when you use tasks.

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

using namespace concurrency;
using namespace std;

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

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

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

/* Output:
    42
*/

Ao usar a função Concurrency:: create_task , você pode usar a auto palavra-chave em vez de declarar o tipo.When you use the concurrency::create_task function, you can use the auto keyword instead of declaring the type. Por exemplo, considere esse código que cria e imprime a matriz de identidade:For example, consider this code that creates and prints the identity matrix:

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

using namespace concurrency;
using namespace std;

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

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

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

Você pode usar a create_task função para criar a operação equivalente.You can use the create_task function to create the equivalent operation.

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

Se uma exceção for lançada durante a execução de uma tarefa, o tempo de execução realizará marshaling dessa exceção na chamada subsequente para task::get ou task::wait , ou para uma continuação baseada em tarefa.If an exception is thrown during the execution of a task, the runtime marshals that exception in the subsequent call to task::get or task::wait, or to a task-based continuation. Para obter mais informações sobre o mecanismo de manipulação de exceção de tarefa, consulte tratamento de exceção.For more information about the task exception-handling mechanism, see Exception Handling.

Para obter um exemplo que usa task , Concurrency:: task_completion_event, o cancelamento, consulte passo a passos: conectando-se usando tarefas e solicitações HTTP XML.For an example that uses task, concurrency::task_completion_event, cancellation, see Walkthrough: Connecting Using Tasks and XML HTTP Requests. (A task_completion_event classe é descrita mais adiante neste documento.)(The task_completion_event class is described later in this document.)

Dica

Para saber detalhes que são específicos para tarefas em aplicativos UWP, consulte programação assíncrona em c++ e criando operações assíncronas em C++ para aplicativos UWP.To learn details that are specific to tasks in UWP apps, see Asynchronous programming in C++ and Creating Asynchronous Operations in C++ for UWP Apps.

Tarefas de continuaçãoContinuation Tasks

Na programação assíncrona, é muito comum para uma operação assíncrona, após a conclusão, invocar uma segunda operação e passar dados para ela.In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. Tradicionalmente, isso é feito usando métodos de retorno de chamada.Traditionally, this is done by using callback methods. Na Tempo de Execução de Simultaneidade, a mesma funcionalidade é fornecida pelas tarefas de continuação.In the Concurrency Runtime, the same functionality is provided by continuation tasks. Uma tarefa de continuação (também conhecida como uma continuação) é uma tarefa assíncrona que é invocada por outra tarefa, que é conhecida como Antecedent, quando o Antecedent é concluído.A continuation task (also known just as a continuation) is an asynchronous task that is invoked by another task, which is known as the antecedent, when the antecedent completes. Usando as continuações, você pode:By using continuations, you can:

  • Passe dados da antecessora para a continuação.Pass data from the antecedent to the continuation.

  • Especifique as condições precisas sob as quais a continuação é invocada ou não é invocada.Specify the precise conditions under which the continuation is invoked or not invoked.

  • Cancele uma continuação antes que ela seja iniciada ou cooperante enquanto estiver em execução.Cancel a continuation either before it starts or cooperatively while it is running.

  • Forneça dicas sobre como a continuação deve ser agendada.Provide hints about how the continuation should be scheduled. (Isso se aplica somente a aplicativos Plataforma Universal do Windows (UWP).(This applies to Universal Windows Platform (UWP) apps only. Para obter mais informações, consulte criando operações assíncronas em C++ para aplicativos UWP.)For more information, see Creating Asynchronous Operations in C++ for UWP Apps.)

  • Invoque várias continuações da mesma antecessora.Invoke multiple continuations from the same antecedent.

  • Invoque uma continuação quando toda ou qualquer um de vários antecedentes for concluído.Invoke one continuation when all or any of multiple antecedents complete.

  • As continuações de cadeia uma após a outra para qualquer comprimento.Chain continuations one after another to any length.

  • Use uma continuação para tratar as exceções que são geradas pelo Antecedent.Use a continuation to handle exceptions that are thrown by the antecedent.

Esses recursos permitem que você execute uma ou mais tarefas quando a primeira tarefa é concluída.These features enable you to execute one or more tasks when the first task completes. Por exemplo, você pode criar uma continuação que compacta um arquivo após a primeira tarefa lê-lo do disco.For example, you can create a continuation that compresses a file after the first task reads it from disk.

O exemplo a seguir modifica o anterior para usar o método Concurrency:: Task:: then para agendar uma continuação que imprime o valor da tarefa Antecedent quando ela estiver disponível.The following example modifies the previous one to use the concurrency::task::then method to schedule a continuation that prints the value of the antecedent task when it is available.

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

using namespace concurrency;
using namespace std;

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

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

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

/* Output:
    42
*/

Você pode encadear e aninhar tarefas para qualquer comprimento.You can chain and nest tasks to any length. Uma tarefa também pode ter várias continuações.A task can also have multiple continuations. O exemplo a seguir ilustra uma cadeia de continuação básica que incrementa o valor da tarefa anterior três vezes.The following example illustrates a basic continuation chain that increments the value of the previous task three times.

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

using namespace concurrency;
using namespace std;

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

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

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

Uma continuação também pode retornar outra tarefa.A continuation can also return another task. Se não houver nenhum cancelamento, essa tarefa será executada antes da continuação subsequente.If there is no cancellation, then this task is executed before the subsequent continuation. Essa técnica é conhecida como desencapsulamento assíncrono.This technique is known as asynchronous unwrapping. O desencapsulamento assíncrono é útil quando você deseja executar trabalho adicional em segundo plano, mas não deseja que a tarefa atual bloqueie o thread atual.Asynchronous unwrapping is useful when you want to perform additional work in the background, but do not want the current task to block the current thread. (Isso é comum em aplicativos UWP, onde as continuaçãos podem ser executadas no thread da interface do usuário).(This is common in UWP apps, where continuations can run on the UI thread). O exemplo a seguir mostra três tarefas.The following example shows three tasks. A primeira tarefa retorna outra tarefa que é executada antes de uma tarefa de continuação.The first task returns another task that is run before a continuation task.

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

using namespace concurrency;
using namespace std;

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

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

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

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

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

Importante

Quando uma continuação de uma tarefa retorna uma tarefa aninhada do tipo N , a tarefa resultante tem o tipo N , não task<N> e é concluída quando a tarefa aninhada é concluída.When a continuation of a task returns a nested task of type N, the resulting task has the type N, not task<N>, and completes when the nested task completes. Em outras palavras, a continuação executa o desencapsulamento da tarefa aninhada.In other words, the continuation performs the unwrapping of the nested task.

Continuação baseada em valor versus com base em tarefasValue-Based Versus Task-Based Continuations

Dado um task objeto cujo tipo de retorno é T , você pode fornecer um valor do tipo T ou task<T> para suas tarefas de continuação.Given a task object whose return type is T, you can provide a value of type T or task<T> to its continuation tasks. Uma continuação que recebe T o tipo é conhecida como uma continuação baseada em valor.A continuation that takes type T is known as a value-based continuation. Uma continuação baseada em valor é agendada para execução quando a tarefa antecedente é concluída sem erros e não é cancelada.A value-based continuation is scheduled for execution when the antecedent task completes without error and is not canceled. Uma continuação que usa task<T> o tipo como seu parâmetro é conhecida como uma continuação baseada em tarefa.A continuation that takes type task<T> as its parameter is known as a task-based continuation. Uma continuação baseada em tarefas é sempre agendada para execução quando a tarefa antecedente é concluída, mesmo quando a tarefa Antecedent é cancelada ou gera uma exceção.A task-based continuation is always scheduled for execution when the antecedent task finishes, even when the antecedent task is canceled or throws an exception. Em seguida, você pode chamar task::get para obter o resultado da tarefa antecedente.You can then call task::get to get the result of the antecedent task. Se a tarefa Antecedent foi cancelada, o task::get gera Concurrency:: task_canceled.If the antecedent task was canceled, task::get throws concurrency::task_canceled. Se a tarefa antecedenta gerou uma exceção, o task::get relança essa exceção.If the antecedent task threw an exception, task::get rethrows that exception. Uma continuação baseada em tarefa não é marcada como cancelada quando sua tarefa Antecedent é cancelada.A task-based continuation is not marked as canceled when its antecedent task is canceled.

Compondo tarefasComposing Tasks

Esta seção descreve as funções Concurrency:: when_all e concurrency:: when_any , que podem ajudá-lo a compor várias tarefas para implementar padrões comuns.This section describes the concurrency::when_all and concurrency::when_any functions, which can help you compose multiple tasks to implement common patterns.

A função when_allThe when_all Function

A when_all função produz uma tarefa concluída após a conclusão de um conjunto de tarefas.The when_all function produces a task that completes after a set of tasks complete. Essa função retorna um objeto std::vector que contém o resultado de cada tarefa no conjunto.This function returns a std::vector object that contains the result of each task in the set. O exemplo básico a seguir usa when_all para criar uma tarefa que representa a conclusão de três outras tarefas.The following basic example uses when_all to create a task that represents the completion of three other tasks.

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

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

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

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

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

Observação

As tarefas que você passa para when_all devem ser uniformes.The tasks that you pass to when_all must be uniform. Em outras palavras, todos eles devem retornar o mesmo tipo.In other words, they must all return the same type.

Você também pode usar a && sintaxe para produzir uma tarefa concluída após a conclusão de um conjunto de tarefas, conforme mostrado no exemplo a seguir.You can also use the && syntax to produce a task that completes after a set of tasks complete, as shown in the following example.

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

É comum usar uma continuação em conjunto com o when_all para executar uma ação após a conclusão de um conjunto de tarefas.It is common to use a continuation together with when_all to perform an action after a set of tasks finishes. O exemplo a seguir modifica o anterior para imprimir a soma de três tarefas que produzem um int resultado.The following example modifies the previous one to print the sum of three tasks that each produce an int result.

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

Neste exemplo, você também pode especificar task<vector<int>> para produzir uma continuação baseada em tarefa.In this example, you can also specify task<vector<int>> to produce a task-based continuation.

Se qualquer tarefa em um conjunto de tarefas for cancelada ou lançar uma exceção, when_all o será imediatamente concluído e não aguardará a conclusão das tarefas restantes.If any task in a set of tasks is canceled or throws an exception, when_all immediately completes and does not wait for the remaining tasks to finish. Se uma exceção for lançada, o tempo de execução relançará a exceção quando você chamar task::get ou task::wait no objeto de tarefa que when_all retorna.If an exception is thrown, the runtime rethrows the exception when you call task::get or task::wait on the task object that when_all returns. Se mais de uma tarefa for lançada, o tempo de execução escolherá uma delas.If more than one task throws, the runtime chooses one of them. Portanto, certifique-se de observar todas as exceções após a conclusão de todas as tarefas; uma exceção de tarefa sem tratamento faz com que o aplicativo seja encerrado.Therefore, ensure that you observe all exceptions after all tasks complete; an unhandled task exception causes the app to terminate.

Aqui está uma função de utilitário que você pode usar para garantir que seu programa Observe todas as exceções.Here's a utility function that you can use to ensure that your program observes all exceptions. Para cada tarefa no intervalo fornecido, observe_all_exceptions dispara qualquer exceção que ocorreu para ser relançada e, em seguida, assimila essa exceção.For each task in the provided range, observe_all_exceptions triggers any exception that occurred to be rethrown and then swallows that exception.

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

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

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

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

        // Cancel any continuations that occur after this task if any previous task was canceled.
        // Although cancellation is not part of this example, we recommend this pattern for cases that do.
        if (status == canceled)
        {
            cancel_current_task();
        }
    });
}

Considere um aplicativo UWP que usa C++ e XAML e grava um conjunto de arquivos em disco.Consider a UWP app that uses C++ and XAML and writes a set of files to disk. O exemplo a seguir mostra como usar when_all observe_all_exceptions o e o para garantir que o programa Observe todas as exceções.The following example shows how to use when_all and observe_all_exceptions to ensure that the program observes all exceptions.

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

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

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

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

        // Cancel any continuations that occur after this task if any previous task was canceled.
        // Although cancellation is not part of this example, we recommend this pattern for cases that do.
        if (status == canceled)
        {
            cancel_current_task();
        }
    });
}
Para executar este exemploTo run this example
  1. Em MainPage. XAML, adicione um Button controle.In MainPage.xaml, add a Button control.
<Button x:Name="Button1" Click="Button_Click">Write files</Button>
  1. Em MainPage. XAML. h, adicione essas declarações de encaminhamento à private seção da MainPage declaração de classe.In MainPage.xaml.h, add these forward declarations to the private section of the MainPage class declaration.
void Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
concurrency::task<void> WriteFilesAsync(Windows::Storage::StorageFolder^ folder, const std::vector<std::pair<Platform::String^, Platform::String^>>& fileContents);
  1. Em MainPage. XAML. cpp, implemente o Button_Click manipulador de eventos.In MainPage.xaml.cpp, implement the Button_Click event handler.
// select-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

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

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

/* Sample output:
    First task to finish returns 42 and has index 1.
*/
  1. Em MainPage. XAML. cpp, implemente WriteFilesAsync conforme mostrado no exemplo.In MainPage.xaml.cpp, implement WriteFilesAsync as shown in the example.

Dica

when_allé uma função sem bloqueio que produz a task como resultado.when_all is a non-blocking function that produces a task as its result. Ao contrário da tarefa:: Wait, é seguro chamar essa função em um aplicativo UWP no thread Asta (Application STA).Unlike task::wait, it is safe to call this function in a UWP app on the ASTA (Application STA) thread.

A função when_anyThe when_any Function

A when_any função produz uma tarefa que é concluída quando a primeira tarefa em um conjunto de tarefas é concluída.The when_any function produces a task that completes when the first task in a set of tasks completes. Essa função retorna um objeto std::p Air que contém o resultado da tarefa concluída e o índice dessa tarefa no conjunto.This function returns a std::pair object that contains the result of the completed task and the index of that task in the set.

A when_any função é especialmente útil nos seguintes cenários:The when_any function is especially useful in the following scenarios:

  • Operações redundantes.Redundant operations. Considere um algoritmo ou uma operação que possam ser executados de várias maneiras.Consider an algorithm or operation that can be performed in many ways. Você pode usar a when_any função para selecionar a operação que é concluída primeiro e, em seguida, cancelar as operações restantes.You can use the when_any function to select the operation that finishes first and then cancel the remaining operations.

  • Operações intercaladas.Interleaved operations. Você pode iniciar várias operações que todas devem ser concluídas e usar a when_any função para processar os resultados à medida que cada operação é concluída.You can start multiple operations that all must finish and use the when_any function to process results as each operation finishes. Após uma operação ser concluída, você poderá começar uma ou mais tarefas adicionais.After one operation finishes, you can start one or more additional tasks.

  • Operações controladas.Throttled operations. Você pode usar a when_any função para estender o cenário anterior limitando o número de operações simultâneas.You can use the when_any function to extend the previous scenario by limiting the number of concurrent operations.

  • Operações expiradas.Expired operations. Você pode usar a when_any função para selecionar entre uma ou mais tarefas e uma tarefa que é concluída após uma hora específica.You can use the when_any function to select between one or more tasks and a task that finishes after a specific time.

Assim como acontece com when_all o, é comum usar uma continuação que tenha que when_any executar a ação quando o primeiro de um conjunto de tarefas for concluído.As with when_all, it is common to use a continuation that has when_any to perform action when the first in a set of tasks finish. O exemplo básico a seguir usa when_any para criar uma tarefa que é concluída quando a primeira das três outras tarefas é concluída.The following basic example uses when_any to create a task that completes when the first of three other tasks completes.

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

using namespace concurrency;

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

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

   structured_task_group tasks;

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

Neste exemplo, você também pode especificar task<pair<int, size_t>> para produzir uma continuação baseada em tarefa.In this example, you can also specify task<pair<int, size_t>> to produce a task-based continuation.

Observação

Assim como acontece com when_all , as tarefas que você passa para when_any devem retornar o mesmo tipo.As with when_all, the tasks that you pass to when_any must all return the same type.

Você também pode usar a || sintaxe para produzir uma tarefa concluída após a primeira tarefa em um conjunto de tarefas ser concluída, conforme mostrado no exemplo a seguir.You can also use the || syntax to produce a task that completes after the first task in a set of tasks completes, as shown in the following example.

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

Dica

Assim como com when_all , o when_any é sem bloqueio e é seguro chamar em um aplicativo UWP no thread Asta.As with when_all, when_any is non-blocking and is safe to call in a UWP app on the ASTA thread.

Execução de tarefa atrasadaDelayed Task Execution

Às vezes, é necessário atrasar a execução de uma tarefa até que uma condição seja satisfeita ou iniciar uma tarefa em resposta a um evento externo.It is sometimes necessary to delay the execution of a task until a condition is satisfied, or to start a task in response to an external event. Por exemplo, em programação assíncrona, talvez seja necessário iniciar uma tarefa em resposta a um evento de conclusão de e/s.For example, in asynchronous programming, you might have to start a task in response to an I/O completion event.

Duas maneiras de fazer isso é usar uma continuação ou iniciar uma tarefa e aguardar um evento dentro da função de trabalho da tarefa.Two ways to accomplish this are to use a continuation or to start a task and wait on an event inside the task's work function. No entanto, há casos em que não é possível usar uma dessas técnicas.However, there are cases where is it not possible to use one of these techniques. Por exemplo, para criar uma continuação, você deve ter a tarefa antecedente.For example, to create a continuation, you must have the antecedent task. No entanto, se você não tiver a tarefa Antecedent, poderá criar um evento de conclusão de tarefa e depois encadear esse evento de conclusão para a tarefa Antecedent quando ela se tornar disponível.However, if you do not have the antecedent task, you can create a task completion event and later chain that completion event to the antecedent task when it becomes available. Além disso, como uma tarefa em espera também bloqueia um thread, você pode usar eventos de conclusão de tarefas para executar o trabalho quando uma operação assíncrona for concluída e, portanto, liberar um thread.In addition, because a waiting task also blocks a thread, you can use task completion events to perform work when an asynchronous operation completes, and thereby free a thread.

A classe Concurrency:: task_completion_event ajuda a simplificar a composição de tarefas.The concurrency::task_completion_event class helps simplify such composition of tasks. Assim como a task classe, o parâmetro de tipo T é o tipo do resultado produzido pela tarefa.Like the task class, the type parameter T is the type of the result that is produced by the task. Esse tipo pode ser void se a tarefa não retornar um valor.This type can be void if the task does not return a value. TNão é possível usar o const modificador.T cannot use the const modifier. Normalmente, um task_completion_event objeto é fornecido a um thread ou a uma tarefa que o sinalizará quando o valor para ele ficar disponível.Typically, a task_completion_event object is provided to a thread or task that will signal it when the value for it becomes available. Ao mesmo tempo, uma ou mais tarefas são definidas como ouvintes desse evento.At the same time, one or more tasks are set as listeners of that event. Quando o evento é definido, as tarefas do ouvinte são concluídas e suas continuaçãos são agendadas para execução.When the event is set, the listener tasks complete and their continuations are scheduled to run.

Para obter um exemplo que usa o task_completion_event para implementar uma tarefa que é concluída após um atraso, consulte como: criar uma tarefa que é concluída após um atraso.For an example that uses task_completion_event to implement a task that completes after a delay, see How to: Create a Task that Completes After a Delay.

Grupos de tarefasTask Groups

Um grupo de tarefas organiza uma coleção de tarefas.A task group organizes a collection of tasks. Os grupos de tarefas enviam tarefas por push para uma fila de roubo de trabalho.Task groups push tasks on to a work-stealing queue. O Agendador remove as tarefas dessa fila e as executa nos recursos de computação disponíveis.The scheduler removes tasks from this queue and executes them on available computing resources. Depois de adicionar tarefas a um grupo de tarefas, você pode aguardar a conclusão de todas as tarefas ou cancelar tarefas que ainda não foram iniciadas.After you add tasks to a task group, you can wait for all tasks to finish or cancel tasks that have not yet started.

A PPL usa as classes Concurrency:: task_group e concurrency:: structured_task_group para representar grupos de tarefas e a classe Concurrency:: task_handle para representar as tarefas que são executadas nesses grupos.The PPL uses the concurrency::task_group and concurrency::structured_task_group classes to represent task groups, and the concurrency::task_handle class to represent the tasks that run in these groups. A task_handle classe encapsula o código que executa o trabalho.The task_handle class encapsulates the code that performs work. Assim como a task classe, a função Work vem na forma de uma função lambda, um ponteiro de função ou um objeto de função.Like the task class, the work function comes in the form of a lambda function, function pointer, or function object. Normalmente, você não precisa trabalhar com task_handle objetos diretamente.You typically do not need to work with task_handle objects directly. Em vez disso, você passa funções de trabalho para um grupo de tarefas e o grupo de tarefas cria e gerencia os task_handle objetos.Instead, you pass work functions to a task group, and the task group creates and manages the task_handle objects.

A PPL divide grupos de tarefas nessas duas categorias: grupos de tarefas não estruturados e grupos de tarefas estruturados.The PPL divides task groups into these two categories: unstructured task groups and structured task groups. A PPL usa a task_group classe para representar grupos de tarefas não estruturados e a structured_task_group classe para representar grupos de tarefas estruturados.The PPL uses the task_group class to represent unstructured task groups and the structured_task_group class to represent structured task groups.

Importante

A PPL também define o algoritmo Concurrency::p arallel_invoke , que usa a structured_task_group classe para executar um conjunto de tarefas em paralelo.The PPL also defines the concurrency::parallel_invoke algorithm, which uses the structured_task_group class to execute a set of tasks in parallel. Como o parallel_invoke algoritmo tem uma sintaxe mais sucinta, recomendamos que você o use em vez da structured_task_group classe quando puder.Because the parallel_invoke algorithm has a more succinct syntax, we recommend that you use it instead of the structured_task_group class when you can. O tópico algoritmos paralelos descreve parallel_invoke mais detalhadamente.The topic Parallel Algorithms describes parallel_invoke in greater detail.

Use parallel_invoke quando você tiver várias tarefas independentes que deseja executar ao mesmo tempo e precisar aguardar a conclusão de todas as tarefas antes de continuar.Use parallel_invoke when you have several independent tasks that you want to execute at the same time, and you must wait for all tasks to finish before you continue. Essa técnica geralmente é conhecida como paralelismo de bifurcação e junção .This technique is often referred to as fork and join parallelism. Use task_group quando você tiver várias tarefas independentes que deseja executar ao mesmo tempo, mas quiser aguardar até que as tarefas sejam concluídas posteriormente.Use task_group when you have several independent tasks that you want to execute at the same time, but you want to wait for the tasks to finish at a later time. Por exemplo, você pode adicionar tarefas a um task_group objeto e aguardar que as tarefas sejam concluídas em outra função ou em outro thread.For example, you can add tasks to a task_group object and wait for the tasks to finish in another function or from another thread.

Os grupos de tarefas dão suporte ao conceito de cancelamento.Task groups support the concept of cancellation. O cancelamento permite sinalizar a todas as tarefas ativas que você deseja cancelar a operação geral.Cancellation enables you to signal to all active tasks that you want to cancel the overall operation. O cancelamento também impede que as tarefas que ainda não começaram a ser iniciadas.Cancellation also prevents tasks that have not yet started from starting. Para obter mais informações sobre cancelamento, consulte cancelamento na ppl.For more information about cancellation, see Cancellation in the PPL.

O tempo de execução também fornece um modelo de tratamento de exceções que permite lançar uma exceção de uma tarefa e lidar com essa exceção quando você aguarda a conclusão do grupo de tarefas associado.The runtime also provides an exception-handling model that enables you to throw an exception from a task and handle that exception when you wait for the associated task group to finish. Para obter mais informações sobre esse modelo de tratamento de exceções, consulte tratamento de exceção.For more information about this exception-handling model, see Exception Handling.

Comparando task_group a structured_task_groupComparing task_group to structured_task_group

Embora seja recomendável usar task_group ou, parallel_invoke em vez da structured_task_group classe, há casos em que você deseja usar structured_task_group , por exemplo, quando você escreve um algoritmo paralelo que executa um número variável de tarefas ou requer suporte para cancelamento.Although we recommend that you use task_group or parallel_invoke instead of the structured_task_group class, there are cases where you want to use structured_task_group, for example, when you write a parallel algorithm that performs a variable number of tasks or requires support for cancellation. Esta seção explica as diferenças entre as task_group structured_task_group classes e.This section explains the differences between the task_group and structured_task_group classes.

A task_group classe é thread-safe.The task_group class is thread-safe. Portanto, você pode adicionar tarefas a um task_group objeto de vários threads e aguardar ou cancelar um task_group objeto de vários threads.Therefore you can add tasks to a task_group object from multiple threads and wait on or cancel a task_group object from multiple threads. A construção e a destruição de um structured_task_group objeto devem ocorrer no mesmo escopo léxico.The construction and destruction of a structured_task_group object must occur in the same lexical scope. Além disso, todas as operações em um structured_task_group objeto devem ocorrer no mesmo thread.In addition, all operations on a structured_task_group object must occur on the same thread. A exceção a essa regra são os métodos Concurrency:: structured_task_group:: Cancel e concurrency:: structured_task_group:: is_canceling .The exception to this rule is the concurrency::structured_task_group::cancel and concurrency::structured_task_group::is_canceling methods. Uma tarefa filho pode chamar esses métodos para cancelar o grupo de tarefas pai ou verificar a cancelamento a qualquer momento.A child task can call these methods to cancel the parent task group or check for cancelation at any time.

Você pode executar tarefas adicionais em um task_group objeto depois de chamar o método Concurrency:: task_group:: Wait ou Concurrency:: task_group:: run_and_wait .You can run additional tasks on a task_group object after you call the concurrency::task_group::wait or concurrency::task_group::run_and_wait method. Por outro lado, se você executar tarefas adicionais em um structured_task_group objeto depois de chamar os métodos Concurrency:: structured_task_group:: Wait ou Concurrency:: structured_task_group:: run_and_wait , o comportamento será indefinido.Conversely, if you run additional tasks on a structured_task_group object after you call the concurrency::structured_task_group::wait or concurrency::structured_task_group::run_and_wait methods, then the behavior is undefined.

Como a structured_task_group classe não é sincronizada entre threads, ela tem menos sobrecarga de execução do que a task_group classe.Because the structured_task_group class does not synchronize across threads, it has less execution overhead than the task_group class. Portanto, se o problema não exigir que você agende o trabalho de vários threads e não possa usar o parallel_invoke algoritmo, a structured_task_group classe poderá ajudá-lo a escrever um código de melhor desempenho.Therefore, if your problem does not require that you schedule work from multiple threads and you cannot use the parallel_invoke algorithm, the structured_task_group class can help you write better performing code.

Se você usar um structured_task_group objeto dentro de outro structured_task_group objeto, o objeto interno deverá ser concluído e destruído antes que o objeto externo seja concluído.If you use one structured_task_group object inside another structured_task_group object, the inner object must finish and be destroyed before the outer object finishes. A task_group classe não exige que os grupos de tarefas aninhados sejam concluídos antes de o grupo externo ser concluído.The task_group class does not require for nested task groups to finish before the outer group finishes.

Grupos de tarefas não estruturados e grupos de tarefas estruturados funcionam com identificadores de tarefas de maneiras diferentes.Unstructured task groups and structured task groups work with task handles in different ways. Você pode passar funções de trabalho diretamente para um task_group objeto; o task_group objeto criará e gerenciará o identificador de tarefa para você.You can pass work functions directly to a task_group object; the task_group object will create and manage the task handle for you. A structured_task_group classe requer que você gerencie um task_handle objeto para cada tarefa.The structured_task_group class requires you to manage a task_handle object for each task. Cada task_handle objeto deve permanecer válido durante o tempo de vida de seu structured_task_group objeto associado.Every task_handle object must remain valid throughout the lifetime of its associated structured_task_group object. Use a função Concurrency:: make_task para criar um task_handle objeto, conforme mostrado no exemplo básico a seguir:Use the concurrency::make_task function to create a task_handle object, as shown in the following basic example:

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

using namespace concurrency;
using namespace std;

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

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

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

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

Para gerenciar identificadores de tarefas para casos em que você tem um número variável de tarefas, use uma rotina de alocação de pilha, como _malloca ou uma classe de contêiner, como std::vector.To manage task handles for cases where you have a variable number of tasks, use a stack-allocation routine such as _malloca or a container class, such as std::vector.

O task_group e o structured_task_group cancelamento de suporte.Both task_group and structured_task_group support cancellation. Para obter mais informações sobre cancelamento, consulte cancelamento na ppl.For more information about cancellation, see Cancellation in the PPL.

ExemploExample

O exemplo básico a seguir mostra como trabalhar com grupos de tarefas.The following basic example shows how to work with task groups. Este exemplo usa o parallel_invoke algoritmo para executar duas tarefas simultaneamente.This example uses the parallel_invoke algorithm to perform two tasks concurrently. Cada tarefa adiciona as subtarefas a um task_group objeto.Each task adds sub-tasks to a task_group object. Observe que a task_group classe permite que várias tarefas adicionem tarefas a ela simultaneamente.Note that the task_group class allows for multiple tasks to add tasks to it concurrently.

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

using namespace concurrency;
using namespace std;

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

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

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

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

Veja a seguir um exemplo de saída para este exemplo:The following is sample output for this example:

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

Como o parallel_invoke algoritmo executa tarefas simultaneamente, a ordem das mensagens de saída pode variar.Because the parallel_invoke algorithm runs tasks concurrently, the order of the output messages could vary.

Para obter exemplos completos que mostram como usar o parallel_invoke algoritmo, consulte como: usar Parallel_invoke para escrever uma rotina de classificação paralela e como usar Parallel_invoke para executar operações paralelas.For complete examples that show how to use the parallel_invoke algorithm, see How to: Use parallel_invoke to Write a Parallel Sort Routine and How to: Use parallel_invoke to Execute Parallel Operations. Para obter um exemplo completo que usa a task_group classe para implementar futuros assíncronos, consulte Walkthrough: Implementing Futures.For a complete example that uses the task_group class to implement asynchronous futures, see Walkthrough: Implementing Futures.

Programação robustaRobust Programming

Certifique-se de entender a função de cancelamento e tratamento de exceções ao usar tarefas, grupos de tarefas e algoritmos paralelos.Make sure that you understand the role of cancellation and exception handling when you use tasks, task groups, and parallel algorithms. Por exemplo, em uma árvore de trabalho paralelo, uma tarefa cancelada impede a execução de tarefas filho.For example, in a tree of parallel work, a task that is canceled prevents child tasks from running. Isso pode causar problemas se uma das tarefas filho executar uma operação que é importante para seu aplicativo, como liberar um recurso.This can cause problems if one of the child tasks performs an operation that is important to your application, such as freeing a resource. Além disso, se uma tarefa filho lançar uma exceção, essa exceção poderá se propagar por meio de um destruidor de objeto e causar um comportamento indefinido em seu aplicativo.In addition, if a child task throws an exception, that exception could propagate through an object destructor and cause undefined behavior in your application. Para obter um exemplo que ilustra esses pontos, consulte a seção entender como o cancelamento e o tratamento de exceções afetam a destruição de objetos nas práticas recomendadas no documento da biblioteca de padrões paralelos.For an example that illustrates these points, see the Understand how Cancellation and Exception Handling Affect Object Destruction section in the Best Practices in the Parallel Patterns Library document. Para obter mais informações sobre os modelos de cancelamento e tratamento de exceção na PPL, consulte cancelamento e tratamento de exceção.For more information about the cancellation and exception-handling models in the PPL, see Cancellation and Exception Handling.

TítuloTitle DescriçãoDescription
Como: usar parallel_invoke para gravar uma rotina de classificação paralelaHow to: Use parallel_invoke to Write a Parallel Sort Routine Mostra como usar o parallel_invoke algoritmo para melhorar o desempenho do algoritmo de classificação bitônico.Shows how to use the parallel_invoke algorithm to improve the performance of the bitonic sort algorithm.
Como: usar parallel_invoke para executar operações paralelasHow to: Use parallel_invoke to Execute Parallel Operations Mostra como usar o parallel_invoke algoritmo para melhorar o desempenho de um programa que executa várias operações em uma fonte de dados compartilhada.Shows how to use the parallel_invoke algorithm to improve the performance of a program that performs multiple operations on a shared data source.
Como: criar uma tarefa que é concluída após um atrasoHow to: Create a Task that Completes After a Delay Mostra como usar as task classes, cancellation_token_source , cancellation_token e task_completion_event para criar uma tarefa que é concluída após um atraso.Shows how to use the task, cancellation_token_source, cancellation_token, and task_completion_event classes to create a task that completes after a delay.
Walkthrough: implementando futurosWalkthrough: Implementing Futures Mostra como combinar a funcionalidade existente no Tempo de Execução de Simultaneidade em algo que faz mais.Shows how to combine existing functionality in the Concurrency Runtime into something that does more.
Biblioteca de padrões paralelos (PPL)Parallel Patterns Library (PPL) Descreve a PPL, que fornece um modelo de programação imperativo para o desenvolvimento de aplicativos simultâneos.Describes the PPL, which provides an imperative programming model for developing concurrent applications.

ReferênciaReference

Classe Task (Tempo de Execução de Simultaneidade)task Class (Concurrency Runtime)

Classe task_completion_eventtask_completion_event Class

Função when_allwhen_all Function

Função when_anywhen_any Function

Classe task_grouptask_group Class

Função parallel_invokeparallel_invoke Function

Classe structured_task_groupstructured_task_group Class