Postupy: Přerušení paralelní smyčky pomocí zpracování výjimek

Toto téma ukazuje, jak napsat vyhledávací algoritmus pro základní stromovou strukturu.

Téma Zrušení vysvětluje roli zrušení v knihovně paralelních vzorů. Použití zpracování výjimek je méně efektivní způsob zrušení paralelní práce než použití metod concurrency::task_group::cancel a concurrency::structured_task_group::cancel . Jeden scénář, ve kterém je však vhodné použít zpracování výjimek ke zrušení práce, je, když zavoláte do knihovny třetí strany, která používá úlohy nebo paralelní algoritmy, ale neposkytuje task_group nebo structured_task_group objekt ke zrušení.

Příklad: Základní typ stromu

Následující příklad ukazuje základní tree typ, který obsahuje datový prvek a seznam podřízených uzlů. Následující část ukazuje tělo for_all metody, která rekurzivně provádí pracovní funkci na každém podřízeném uzlu.

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

Příklad: Paralelní provádění práce

Následující příklad ukazuje metodu for_all . Používá souběžnost::p arallel_for_each algoritmus k provádění pracovní funkce na každém uzlu stromu paralelně.

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

Příklad: Vyhledání hodnoty ve stromu

Následující příklad ukazuje search_for_value funkci, která hledá hodnotu v zadaném tree objektu. Tato funkce předá for_all metodě pracovní funkci, která vyvolá, když najde uzel stromu, který obsahuje zadanou hodnotu.

Předpokládejme, že tree třída je poskytována knihovnou třetí strany a že ji nemůžete upravit. V tomto případě je použití zpracování výjimek vhodné, protože for_all metoda neposkytuje volajícímu task_group objekt ani structured_task_group objekt. Proto pracovní funkce nemůže přímo zrušit nadřazenou skupinu úloh.

Když pracovní funkce, kterou zadáte skupině úloh, vyvolá výjimku, modul runtime zastaví všechny úkoly, které jsou ve skupině úkolů (včetně všech podřízených skupin úkolů) a zahodí všechny úkoly, které ještě nebyly spuštěny. Funkce search_for_value používá try-catch blok k zachycení výjimky a tisku výsledku do konzoly.

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

Příklad: Paralelní vytvoření a hledání stromu

Následující příklad vytvoří tree objekt a vyhledá ho paralelně několik hodnot. Funkce build_tree se zobrazí dále v tomto tématu.

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

Tento příklad používá algoritmus concurrency::p arallel_invoke k paralelnímu hledání hodnot. Další informace o tomto algoritmu naleznete v tématu Paralelní algoritmy.

Příklad: Dokončené zpracování výjimek – ukázka kódu

Následující úplný příklad používá zpracování výjimek k hledání hodnot v základní stromové struktuře.

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

Tento příklad vytvoří následující ukázkový výstup.

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

Probíhá kompilace kódu

Zkopírujte ukázkový kód a vložte ho do projektu sady Visual Studio nebo ho vložte do pojmenovaného task-tree-search.cpp souboru a potom v okně příkazového řádku sady Visual Studio spusťte následující příkaz.

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

Viz také

Zrušení v knihovně PPL
Zpracování výjimek
Paralelismus úkolu
Paralelní algoritmy
task_group – třída
structured_task_group – třída
parallel_for_each – funkce