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.Die Verwendung der Ausnahmebehandlung ist weniger effizient, parallele Arbeitsvorgänge als die Verwendung von den concurrency::task_group::cancel und concurrency::structured_task_group::cancel-Methoden abzubrechen.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(begin(_children), end(_children), [&](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 der concurrency::parallel_invoke Algorithmus, um 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(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.
Kompilieren des Codes
Kopieren Sie den Beispielcode und fügen Sie ihn in einem Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei, die task-tree-search.cpp namens und dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster ausgeführt.
cl.exe /EHsc task-tree-search.cpp
Siehe auch
Referenz
Konzepte
Ausnahmebehandlung in der Concurrency Runtime