Partilhar via


Como: Use o tratamento de exceção para quebra de um Loop paralelo

Este tópico mostra como escrever um algoritmo de pesquisa para uma estrutura de árvore básico.

O tópico Cancelamento na PPL explica a função de cancelamento na biblioteca de padrões paralelos. O uso de manipulação de exceção é cancelar o trabalho paralelo que o uso de uma maneira menos eficiente do Concurrency::task_group::cancel e Concurrency::structured_task_group::cancel métodos. No entanto, um cenário onde o uso de manipulação de exceção para cancelar o trabalho é apropriado é quando você chama em uma biblioteca de terceiros que usa algoritmos paralelos ou tarefas, mas não fornece um task_group ou structured_task_group o objeto para cancelar.

Exemplo

O exemplo a seguir mostra um basic tree tipo que contém um elemento de dados e uma lista de nós do filho. A seção a seguir mostra o corpo da for_all método, o qual recursivamente executa uma função de trabalho em cada nó filho.

// A simple tree structure that has multiple child nodes.
template <typename T>
class tree
{
public:
   explicit tree(T data)
      : _data(data)
   {
   }

   // Retrieves the data element for the node. 
   T get_data() const
   {
      return _data;
   }

   // Adds a child node to the tree.
   void add_child(tree& child)
   {
      _children.push_back(child);
   }

   // Performs the given work function on the data element of the tree and
   // on each child.
   template<class Function>
   void for_all(Function& action);

private:
   // The data for this node.
   T _data;
   // The child nodes.
   list<tree> _children;
};

A exemplo a seguir mostra a for_all método. Ele usa o Concurrency::parallel_for_each o algoritmo para realizar uma função de trabalho em cada nó da árvore em paralelo.

// Performs the given work function on the data element of the tree and
// on each child.
template<class Function>
void for_all(Function& action)
{
   // Perform the action on each child.
   parallel_for_each(_children.begin(), _children.end(), [&](tree& child) {
      child.for_all(action);
   });

   // Perform the action on this node.
   action(*this);
}

A exemplo a seguir mostra a search_for_value a função, que procura um valor em fornecida tree objeto. Esta função passa para o for_all método uma função de trabalho que lança quando ele localiza um nó de árvore que contém o valor fornecido.

Suponha que o tree classe é fornecida por uma biblioteca de terceiros, e que você não pode modificar o proprietário. Nesse caso, o uso de manipulação de exceção é apropriado porque o for_all método não fornece um task_group ou structured_task_group o objeto para o chamador. Portanto, a função de trabalho é não é possível cancelar diretamente de seu grupo de tarefas do pai.

Quando a função de trabalho que você fornecer para um grupo de tarefas lança uma exceção, o tempo de execução interrompe todas as tarefas que estão no grupo de tarefas (incluindo quaisquer grupos de tarefas filho) e descarta quaisquer tarefas que ainda não iniciaram. O search_for_value função usa um try–catch bloco para capturar a exceção e imprimir o resultado no console.

// Searches for a value in the provided tree object.
template <typename T>
void search_for_value(tree<T>& t, int value)
{
   try
   {
      // Call the for_all method to search for a value. The work function
      // throws an exception when it finds the value.
      t.for_all([value](const tree<T>& node) {
         if (node.get_data() == value)
         {
            throw &node;
         }
      });
   }
   catch (const tree<T>* node)
   {
      // A matching node was found. Print a message to the console.
      wstringstream ss;
      ss << L"Found a node with value " << value << L'.' << endl;
      wcout << ss.str();
      return;
   }

   // A matching node was not found. Print a message to the console.
   wstringstream ss;
   ss << L"Did not find node with value " << value << L'.' << endl;
   wcout << ss.str();   
}

O exemplo a seguir cria um tree de objeto e a procura por diversos valores em paralelo. O build_tree função é mostrada posteriormente neste tópico.

int wmain()
{  
   // Build a tree that is four levels deep with the initial level 
   // having three children. The value of each node is a random number.
   mt19937 gen(38);
   tree<int> t = build_tree<int>(4, 3, [&gen]{ return gen()%100000; });

   // Search for a few values in the tree in parallel.
   parallel_invoke(
      [&t] { search_for_value(t, 86131); },
      [&t] { search_for_value(t, 17522); },
      [&t] { search_for_value(t, 32614); }
   );
}

Este exemplo usa a Concurrency::parallel_invoke o algoritmo para pesquisar valores em paralelo. Para obter mais informações sobre esse algoritmo, consulte Algoritmos paralelos.

O exemplo a seguir completo usa o tratamento de exceção para pesquisar valores em uma estrutura de árvore básico.

// task-tree-search.cpp
// compile with: /EHsc
#include <ppl.h>
#include <list>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <random>

using namespace Concurrency;
using namespace std;

// A simple tree structure that has multiple child nodes.
template <typename T>
class tree
{
public:
   explicit tree(T data)
      : _data(data)
   {
   }

   // Retrieves the data element for the node. 
   T get_data() const
   {
      return _data;
   }

   // Adds a child node to the tree.
   void add_child(tree& child)
   {
      _children.push_back(child);
   }

   // Performs the given work function on the data element of the tree and
   // on each child.
   template<class Function>
   void for_all(Function& action)
   {
      // Perform the action on each child.
      parallel_for_each(_children.begin(), _children.end(), [&](tree& child) {
         child.for_all(action);
      });

      // Perform the action on this node.
      action(*this);
   }

private:
   // The data for this node.
   T _data;
   // The child nodes.
   list<tree> _children;
};

// Builds a tree with the given depth. 
// Each node of the tree is initialized with the provided generator function.
// Each level of the tree has one more child than the previous level.
template <typename T, class Generator>
tree<T> build_tree(int depth, int child_count, Generator& g)
{
   // Create the tree node.
   tree<T> t(g());

   // Add children.
   if (depth > 0)
   {
      for(int i = 0; i < child_count; ++i)
      {
         t.add_child(build_tree<T>(depth - 1, child_count + 1, g));
      }
   }

   return t;
}

// Searches for a value in the provided tree object.
template <typename T>
void search_for_value(tree<T>& t, int value)
{
   try
   {
      // Call the for_all method to search for a value. The work function
      // throws an exception when it finds the value.
      t.for_all([value](const tree<T>& node) {
         if (node.get_data() == value)
         {
            throw &node;
         }
      });
   }
   catch (const tree<T>* node)
   {
      // A matching node was found. Print a message to the console.
      wstringstream ss;
      ss << L"Found a node with value " << value << L'.' << endl;
      wcout << ss.str();
      return;
   }

   // A matching node was not found. Print a message to the console.
   wstringstream ss;
   ss << L"Did not find node with value " << value << L'.' << endl;
   wcout << ss.str();   
}

int wmain()
{  
   // Build a tree that is four levels deep with the initial level 
   // having three children. The value of each node is a random number.
   mt19937 gen(38);
   tree<int> t = build_tree<int>(4, 3, [&gen]{ return gen()%100000; });

   // Search for a few values in the tree in parallel.
   parallel_invoke(
      [&t] { search_for_value(t, 86131); },
      [&t] { search_for_value(t, 17522); },
      [&t] { search_for_value(t, 32614); }
   );
}

Este exemplo produz a saída de exemplo a seguir.

Found a node with value 32614.
Found a node with value 86131.
Did not find node with value 17522.

Compilando o código

Copie o código de exemplo e colá-lo em um Visual Studio do projeto, ou colá-lo em um arquivo que é chamado search.cpp de árvore de tarefa e, em seguida, execute o seguinte comando um Visual Studio 2010 janela do Prompt de comando.

cl.exe /EHsc task-tree-search.cpp

Consulte também

Referência

Classe de task_group

structured_task_group classe

Função de parallel_for_each

Conceitos

Cancelamento na PPL

O Runtime de simultaneidade de manipulação de exceção

Paralelismo de tarefas (Runtime de simultaneidade)

Algoritmos paralelos

Histórico de alterações

Date

History

Motivo

Março de 2011

Esclarecida quando usar manipulação de exceção para cancelar uma operação em paralelo.

Comentários do cliente.