Condividi tramite


Procedura: Usare la gestione delle eccezion per interrompere un ciclo Parallel

In questo argomento viene illustrato come scrivere un algoritmo di ricerca per una struttura ad albero di base.

L'argomento Cancellation illustra il ruolo di annullamento nella libreria Parallel Patterns. L'uso della gestione delle eccezioni è un modo meno efficiente per annullare il lavoro parallelo rispetto all'uso dei metodi concurrency::task_group::cancel e concurrency::structured_task_group::cancel . Tuttavia, uno scenario in cui l'uso della gestione delle eccezioni per annullare il lavoro è appropriato quando si chiama in una libreria di terze parti che usa attività o algoritmi paralleli, ma non fornisce un task_group oggetto o structured_task_group per annullare.

Esempio: Tipo di albero di base

Nell'esempio seguente viene illustrato un tipo di base tree che contiene un elemento dati e un elenco di nodi figlio. La sezione seguente illustra il corpo del for_all metodo , che esegue in modo ricorsivo una funzione di lavoro in ogni nodo figlio.

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

Esempio: Eseguire operazioni in parallelo

Nell'esempio seguente viene illustrato il for_all metodo . Usa l'algoritmo concurrency::p arallel_for_each per eseguire una funzione di lavoro in ogni nodo dell'albero in parallelo.

// 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(begin(_children), end(_children), [&](tree& child) {
      child.for_all(action);
   });

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

Esempio: cercare un valore nell'albero

Nell'esempio seguente viene illustrata la funzione search_for_value che cerca un valore nell'oggetto tree fornito. Questa funzione passa al for_all metodo una funzione di lavoro che genera quando trova un nodo della struttura ad albero che contiene il valore specificato.

Si supponga che la tree classe sia fornita da una libreria di terze parti e che non sia possibile modificarla. In questo caso, l'uso della gestione delle eccezioni è appropriato perché il for_all metodo non fornisce un task_group oggetto o structured_task_group al chiamante. Pertanto, la funzione di lavoro non è in grado di annullare direttamente il gruppo di attività padre.

Quando la funzione di lavoro fornita a un gruppo di attività genera un'eccezione, il runtime arresta tutte le attività presenti nel gruppo di attività (inclusi i gruppi di attività figlio) e rimuove tutte le attività che non sono ancora state avviate. La funzione search_for_value usa un blocco try-catch per acquisire l'eccezione e stampare il risultato nella 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();   
}

Esempio: Creare e cercare un albero in parallelo

Nell'esempio seguente viene creato un tree oggetto e viene eseguita la ricerca di diversi valori in parallelo. La build_tree funzione viene illustrata più avanti in questo argomento.

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

In questo esempio viene usato l'algoritmo concurrency::p arallel_invoke per cercare i valori in parallelo. Per altre informazioni su questo algoritmo, vedere Algoritmi paralleli.

Esempio: Esempio di codice di gestione delle eccezioni completato

Nell'esempio completo seguente viene utilizzata la gestione delle eccezioni per cercare i valori in una struttura ad albero di base.

// 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(begin(_children), end(_children), [&](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); }
   );
}

In questo esempio viene generato l'output di esempio seguente.

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

Compilazione del codice

Copiare il codice di esempio e incollarlo in un progetto di Visual Studio oppure incollarlo in un file denominato task-tree-search.cpp e quindi eseguire il comando seguente in una finestra del prompt dei comandi di Visual Studio.

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

Vedi anche

Annullamento nella libreria PPL
Gestione delle eccezioni
Parallelismo delle attività
Algoritmi paralleli
Classe task_group
Classe structured_task_group
Funzione parallel_for_each