Paralelismo de tarefas (Runtime de simultaneidade)

Este documento descreve a função de tarefas e grupos de tarefas no Runtime de simultaneidade. Use grupos de tarefas quando você tem dois ou mais itens de trabalho independentes que você deseja executar simultaneamente. Por exemplo, suponha que você tem um algoritmo recursivo que divide o trabalho restante em duas partições. Você pode usar grupos de tarefas para executar essas partições simultaneamente. Por outro lado, use os algoritmos paralelos, como Concurrency::parallel_for, quando você deseja aplicar a mesma rotina para cada elemento de uma coleção em paralelo. Para obter mais informações sobre algoritmos paralelos, consulte Algoritmos paralelos.

Grupos de tarefas e tarefas

A tarefa é uma unidade de trabalho que executa um trabalho específico. Uma tarefa normalmente pode ser executado em paralelo com outras tarefas e pode ser decomposta em tarefas adicionais, mais refinadas. A grupo de tarefas organiza um conjunto de tarefas. Grupos de tarefas empurre tarefas para uma fila de roubo de trabalho. O Agendador remove tarefas da fila e executa em recursos de computação disponíveis. Depois de adicionar tarefas a um grupo de tarefas, você pode esperar para todas as tarefas concluir ou cancelar as tarefas que ainda não iniciaram.

A PPL usa a Concurrency::task_group e Concurrency::structured_task_group classes para representar grupos de tarefas e o Concurrency::task_handle classe para representar as tarefas. O task_handle classe encapsula o código que executa o trabalho. Esse código é fornecido na forma de uma função lambda, um ponteiro de função ou um objeto de função e é conhecido como um a função de trabalho. Você normalmente não precisa trabalhar com task_handle objetos diretamente. Em vez disso, você passa a funções de trabalho para um grupo de tarefas e o grupo de tarefas cria e gerencia o task_handle objetos.

A PPL divide a grupos de tarefas dessas duas categorias: grupos de tarefas não-estruturados e grupos de tarefas estruturadas. A PPL usa a task_group classe para representar grupos de tarefas não-estruturado e o structured_task_group classe para representar grupos de tarefas estruturadas.

Observação importanteImportante

A PPL também define o Concurrency::parallel_invoke algoritmo, que utiliza o structured_task_group classe para executar um conjunto de tarefas em paralelo. Porque o parallel_invoke algoritmo possui uma sintaxe mais sucinta, recomendamos que você usá-lo em vez da structured_task_group classe quando você can. O tópico Algoritmos paralelos descreve parallel_invoke em maior detalhe.

Use parallel_invoke Quando você tem várias tarefas independentes que você deseja executar ao mesmo tempo, e você deve esperar para todas as tarefas concluir antes de continuar. Use task_group Quando você tem várias tarefas independentes que você deseja executar ao mesmo tempo, mas deseja aguardar as finalização de tarefas em um momento posterior. Por exemplo, você pode adicionar tarefas para um task_group objeto e aguardar as finalização de tarefas em outra função ou de outro thread.

Suportam a grupos de tarefas o conceito de cancelamento. Cancelamento permite Sinalizar para todas as tarefas ativas que você deseja cancelar a operação geral. Cancelamento também impede que as tarefas que ainda não iniciaram seja iniciado. Para obter mais informações sobre o cancelamento, consulte Cancelamento na PPL.

O runtime também fornece um modelo de manipulação de exceção que permite que você lance uma exceção de uma tarefa e tratar essa exceção quando você aguarde o grupo de tarefas associadas ao fim. Para obter mais informações sobre esse modelo de manipulação de exceção, consulte O Runtime de simultaneidade de manipulação de exceção.

Comparando task_group para structured_task_group

Apesar de recomendarmos que você use task_group ou parallel_invoke em vez da structured_task_group da classe, existem casos onde você pode usar structured_task_group, por exemplo, quando você escreve um algoritmo paralelo que realiza um número variável de tarefas ou requer suporte para cancelamento. Esta seção explica as diferenças entre o task_group e structured_task_group classes.

O task_group classe é thread-safe. Portanto, você pode adicionar tarefas para um task_group de objeto de vários threads e aguardar ou cancelar um task_group objeto a partir de vários segmentos. A construção e a destruição de um structured_task_group objeto deve ocorrer o mesmo escopo léxico. Além disso, todas as operações em um structured_task_group objeto deve ocorrer no mesmo segmento. A exceção a essa regra é o Concurrency::structured_task_group::cancel e Concurrency::structured_task_group::is_canceling métodos. Uma tarefa filho pode chamar esses métodos para cancelar o grupo de tarefas do pai ou procurar o cancelamento a qualquer momento.

Você pode executar tarefas adicionais em um task_group objeto depois de chamar o Concurrency::task_group::wait ou Concurrency::task_group::run_and_wait método. Por outro lado, é possível executar tarefas adicionais em um structured_task_group objeto depois de chamar o Concurrency::structured_task_group::wait ou simultaneidade:: structured_task_group::run_and_wait métodos.

Porque o structured_task_group classe não sincronizar entre threads, ele tem menos execução sobrecarga que o task_group classe. Portanto, se o seu problema não requer que você agendar um trabalho de vários threads e você não pode usar o parallel_invoke o algoritmo, o structured_task_group classe pode ajudar a escrever código de melhor desempenho.

Se você usar um structured_task_group o objeto dentro de outro structured_task_group o objeto, o objeto interno deve terminar e ser destruído antes da conclusão do objeto externo. O task_group classe não exige a grupos de tarefas aninhado termine antes de terminar o grupo externo.

Grupos de tarefas não-estruturados e estruturados tarefa funcionam com alças de tarefa de maneiras diferentes. Você pode passar a funções de trabalho diretamente a um task_group objeto; o task_group objeto irá criar e gerenciar o indicador de tarefa para você. O structured_task_group classe requer que você gerencie um task_handle o objeto para cada tarefa. Cada task_handle objeto deve permanecer válido em toda a vida útil de seu associado structured_task_group objeto. Use o Concurrency::make_task função para criar um task_handle o objeto, como mostrado no seguinte exemplo básico:

// 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);
}

Para gerenciar os identificadores de tarefa para casos em que um número variável de tarefas, use uma rotina de alocação de pilha como _malloca ou classe de um recipiente, como std:: Vector.

Ambos task_group e structured_task_group suporte ao cancelamento. Para obter mais informações sobre o cancelamento, consulte Cancelamento na PPL.

Exemplo

O exemplo básico a seguir mostra como trabalhar com grupos de tarefas. Este exemplo usa a parallel_invoke o algoritmo executar duas tarefas simultaneamente. Cada tarefa adiciona subtarefas para um task_group objeto. Observe que o task_group classe permite a várias tarefas adicionar tarefas nele simultaneamente.

// 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();
}

Este é o exemplo de saída para este exemplo:

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

Porque o parallel_invoke algoritmo executa tarefas simultaneamente, a ordem das mensagens de saída pode variar.

Para concluir exemplos que mostram como usar o parallel_invoke algoritmo, consulte Como: Use o parallel_invoke para escrever uma rotina de classificação paralela e Como: Use o parallel_invoke para executar operações paralelas. Para obter um exemplo completo que usa o task_group classe para implementar futuros assíncronos, consulte Demonstra Passo a passo: A implementação de futuros.

Programação robusta

Certifique-se de que você compreenda a função de cancelamento e a manipulação de exceção, quando você usa grupos de tarefas e algoritmos paralelos. Por exemplo, em uma árvore de trabalho paralelo, uma tarefa que seja cancelada impede tarefas filho em execução. Isso pode causar problemas se uma das tarefas filho realiza uma operação que é importante para seu aplicativo, como, por exemplo, liberando um recurso. Além disso, se uma tarefa filho lança uma exceção, essa exceção pode propagar através de um destruidor de objeto e causar um comportamento indefinido em seu aplicativo. Para obter um exemplo que ilustra esses pontos, consulte o Understand how Cancellation and Exception Handling Affect Object Destruction seção práticas recomendadas do documento da biblioteca de padrões paralelos. Para obter mais informações sobre o cancelamento e modelos de manipulação de exceção na PPL, consulte Cancelamento na PPL e O Runtime de simultaneidade de manipulação de exceção.

Tópicos relacionados

Referência

Classe de task_group

Função de parallel_invoke

structured_task_group classe

Histórico de alterações

Date

History

Motivo

Março de 2011

Adicionadas informações sobre a função de cancelamento e ao usar grupos de tarefas e algoritmos paralelos de manipulação de exceção.

Aprimoramento de informações.

Julho de 2010

Conteúdo reorganizado.

Aprimoramento de informações.

Maio de 2010

Diretrizes expandidas.

Aprimoramento de informações.