Gewusst wie: Verwenden der Ausnahmebehandlung zum Verlassen einer Parallel-Schleife

In diesem Thema wird gezeigt, wie Sie einen Suchalgorithmus für eine grundlegende Struktur schreiben.

Im Thema "Abbruch " wird die Rolle des Abbruchs in der Parallel Patterns Library erläutert. Die Verwendung der Ausnahmebehandlung ist eine weniger effiziente Möglichkeit, parallele Arbeit abzubrechen als die Verwendung der Parallelität::task_group::cancel und parallelcurrency::structured_task_group::cancel-Methoden . Ein Szenario, in dem die Verwendung der Ausnahmebehandlung zum Abbrechen der Arbeit angemessen ist, ist jedoch der Fall, wenn Sie sich in eine Drittanbieterbibliothek einwählen, die Aufgaben oder parallele Algorithmen verwendet, aber kein Objekt zum structured_task_group Abbrechen bereitstellttask_group.

Beispiel: Basisstrukturtyp

Das folgende Beispiel zeigt einen grundlegenden tree Typ, der ein Datenelement und eine Liste untergeordneter Knoten enthält. Der folgende Abschnitt zeigt den Textkörper der for_all Methode, die rekursiv eine Arbeitsfunktion für jeden untergeordneten Knoten ausführt.

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

Beispiel: Paralleles Ausführen von Arbeiten

Das folgende Beispiel zeigt die for_all Methode. Es verwendet die Parallelität::p arallel_for_each Algorithmus, um eine Arbeitsfunktion für jeden Knoten der Struktur parallel auszuführen.

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

Beispiel: Durchsuchen der Struktur nach einem Wert

Das folgende Beispiel zeigt die search_for_value-Funktion, die nach einem Wert im bereitgestellten tree-Objekt sucht. Diese Funktion übergibt an die for_all Methode eine Arbeitsfunktion, die ausgelöst wird, wenn ein Strukturknoten gefunden wird, der den bereitgestellten Wert enthält.

Gehen Sie davon aus, dass die tree Klasse von einer Drittanbieterbibliothek bereitgestellt wird und Sie sie nicht ändern können. In diesem Fall ist die Verwendung der Ausnahmebehandlung geeignet, da die for_all Methode dem Aufrufer kein Objekt oder structured_task_group Objekt task_group bereitstellt. Daher kann die Arbeitsfunktion ihre übergeordnete Aufgabengruppe nicht direkt abbrechen.

Wenn die Arbeitsfunktion, die Sie für eine Aufgabengruppe bereitstellen, eine Ausnahme auslöst, beendet die Laufzeit alle Aufgaben, die sich in der Aufgabengruppe befinden (einschließlich aller untergeordneten Aufgabengruppen), und Karte dis alle Aufgaben, die noch nicht gestartet wurden. Die search_for_value-Funktion verwendet einen try-catch-Block, um die Ausnahme zu erfassen und das Ergebnis in der Konsole auszugeben.

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

Beispiel: Erstellen und Durchsuchen einer Struktur parallel

Im folgenden Beispiel wird ein tree Objekt erstellt und parallel nach mehreren Werten durchsucht. Die build_tree Funktion wird weiter unten in diesem Thema gezeigt.

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 diesem Beispiel wird der Algorithmus "concurrency::p arallel_invoke " verwendet, um parallel nach Werten zu suchen. Weitere Informationen zu diesem Algorithmus finden Sie unter Parallel-Algorithmen.

Beispiel: Fertiges Codebeispiel für die Ausnahmebehandlung

Im folgenden vollständigen Beispiel wird die Ausnahmebehandlung verwendet, um nach Werten in einer einfachen Struktur zu suchen.

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

Dieses Beispiel erzeugt die folgende Beispielausgabe.

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

Kompilieren des Codes

Kopieren Sie den Beispielcode, fügen Sie ihn in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei ein, die benannt task-tree-search.cpp ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus.

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

Siehe auch

Abbruch in der PPL
Ausnahmebehandlung
Task-Parallelität
Parallele Algorithmen
task_group-Klasse
structured_task_group-Klasse
parallel_for_each-Funktion