Share via


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

In diesem Thema wird erläutert, wie ein Suchalgorithmus für eine einfache Baumstruktur geschrieben wird.

Das Thema Abbruch in der PPL erläutert die Rolle des Abbruchs in der Parallel Patterns Library. Das Abbrechen paralleler Arbeitsvorgänge mit der Ausnahmebehandlung ist weniger effizient als die Verwendung der Concurrency::task_group::cancel- und der Concurrency::structured_task_group::cancel-Methode zum gleichen Zweck. Die Verwendung der Ausnahmebehandlung zum Abbrechen eines Arbeitsvorgangs ist jedoch angemessen, wenn Sie die Bibliothek eines Drittanbieters mit Aufgaben oder parallelen Algorithmen aufrufen, diese jedoch kein task_group- oder structured_task_group-Objekt zum Abbrechen bereitstellt.

Beispiel

Im folgenden Beispiel wird ein grundlegender tree-Typ dargestellt, der ein Datenelement und eine Liste untergeordneter Knoten enthält. Der folgende Abschnitt zeigt den Text der for_all-Methode an, der 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;
};

Im folgenden Beispiel wird die for_all-Methode dargestellt. Sie verwendet den Concurrency::parallel_for_each-Algorithmus, um eine Arbeitsfunktion für jeden Knoten in der Baumstruktur 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(_children.begin(), _children.end(), [&](tree& child) {
      child.for_all(action);
   });

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

Im folgenden Beispiel wird die search_for_value-Funktion veranschaulicht, die nach einem Wert im bereitgestellten tree-Objekt sucht. Diese Funktion übergibt eine Arbeitsfunktion an die for_all-Methode; diese Arbeitsfunktion wird ausgelöst, wenn sie auf einen Strukturknoten trifft, der den bereitgestellten Wert enthält.

Angenommen, die tree-Klasse wird von der Bibliothek eines Drittanbieters bereitgestellt und kann nicht geändert werden. Die Verwendung der Ausnahmebehandlung ist für dieses Beispiel geeignet, da die for_all-Methode dem Aufrufer kein task_group-Objekt oder structured_task_group-Objekt bereitstellt. Daher kann die Arbeitsfunktion nicht direkt ihre übergeordnete Aufgabengruppe abbrechen.

Wenn die Arbeitsfunktion, die Sie für eine Aufgabengruppe bereitstellen, eine Ausnahme auslöst, beendet die Laufzeit alle Aufgaben in der Aufgabengruppe (einschließlich aller untergeordneten Aufgabengruppen) und verwirft 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();   
}

Im folgenden Beispiel wird ein tree-Objekt erstellt und darin parallel nach mehreren Werten gesucht. Die build_tree-Funktion wird weiter unten in diesem Thema dargestellt.

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 mit dem Concurrency::parallel_invoke-Algorithmus parallel nach Werten gesucht. Weitere Informationen zu diesem Algorithmus finden Sie unter Parallele Algorithmen.

Im folgenden vollständigen Beispiel wird anhand der Ausnahmebehandlung nach Werten in einer einfachen Baumstruktur gesucht.

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

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, und fügen Sie ihn in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei mit dem Namen task-tree-search.cpp ein, und führen Sie dann den folgenden Befehl in einem Visual Studio 2010-Eingabeaufforderungsfenster aus.

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

Siehe auch

Referenz

task_group-Klasse

structured_task_group-Klasse

parallel_for_each-Funktion

Konzepte

Abbruch in der PPL

Ausnahmebehandlung in der Concurrency Runtime

Aufgabenparallelität (Concurrency Runtime)

Parallele Algorithmen

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

März 2011

Verwendung der Ausnahmebehandlung zum Abbrechen eines parallelen Vorgangs verdeutlicht.

Kundenfeedback.