Porady: Użyj obsługi wyjątków, aby przerwać pętlę równoległą
W tym temacie przedstawiono sposób pisania algorytmu wyszukiwania dla podstawowej struktury drzewa.
W temacie Anulowanie wyjaśniono rolę anulowania w bibliotece wzorców równoległych. Użycie obsługi wyjątków jest mniej wydajnym sposobem anulowania równoległej pracy niż użycie metod concurrency::task_group::cancel i concurrency::structured_task_group::cancel . Jednak jednym ze scenariuszy, w których użycie obsługi wyjątków do anulowania pracy jest odpowiednie, jest wywołanie do biblioteki innej firmy, która używa zadań lub algorytmów równoległych, ale nie udostępnia task_group
obiektu lub structured_task_group
do anulowania.
Przykład: podstawowy typ drzewa
W poniższym przykładzie przedstawiono podstawowy tree
typ zawierający element danych i listę węzłów podrzędnych. W poniższej sekcji przedstawiono treść for_all
metody, która rekursywnie wykonuje funkcję pracy w każdym węźle podrzędnym.
// 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;
};
Przykład: równoległe wykonywanie pracy
W poniższym przykładzie przedstawiono metodę for_all
. Używa on współbieżności::p arallel_for_each algorytmu do równoległego wykonywania funkcji pracy w każdym węźle drzewa.
// 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);
}
Przykład: wyszukiwanie drzewa pod kątem wartości
Poniższy przykład przedstawia search_for_value
funkcję, która wyszukuje wartość w podanym tree
obiekcie. Ta funkcja przekazuje do for_all
metody funkcję pracy, która zgłasza błąd, gdy znajdzie węzeł drzewa, który zawiera podaną wartość.
Załóżmy, że tree
klasa jest dostarczana przez bibliotekę innej firmy i że nie można jej modyfikować. W takim przypadku użycie obsługi wyjątków jest odpowiednie, ponieważ for_all
metoda nie udostępnia task_group
obiektu lub structured_task_group
obiektu obiektu wywołującego. W związku z tym funkcja robocza nie może bezpośrednio anulować nadrzędnej grupy zadań.
Gdy funkcja pracy udostępniana grupie zadań zgłasza wyjątek, środowisko uruchomieniowe zatrzymuje wszystkie zadania w grupie zadań (w tym wszystkie podrzędne grupy zadań) i odrzuca wszystkie zadania, które nie zostały jeszcze uruchomione. Funkcja search_for_value
używa try
-catch
bloku do przechwytywania wyjątku i drukowania wyniku w konsoli.
// 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();
}
Przykład: równoległe tworzenie i wyszukiwanie drzewa
Poniższy przykład tworzy tree
obiekt i wyszukuje kilka wartości równolegle. Funkcja jest wyświetlana build_tree
w dalszej części tego tematu.
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); }
);
}
W tym przykładzie użyto algorytmu concurrency::p arallel_invoke , aby wyszukać wartości równolegle. Aby uzyskać więcej informacji na temat tego algorytmu, zobacz Parallel Algorithms (Algorytmy równoległe).
Przykład: Ukończono przykład kodu obsługi wyjątków
Poniższy kompletny przykład używa obsługi wyjątków do wyszukiwania wartości w podstawowej strukturze drzewa.
// 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); }
);
}
W tym przykładzie są generowane następujące przykładowe dane wyjściowe.
Found a node with value 32614.
Found a node with value 86131.
Did not find node with value 17522.
Kompilowanie kodu
Skopiuj przykładowy kod i wklej go w projekcie programu Visual Studio lub wklej go w pliku o nazwie task-tree-search.cpp
, a następnie uruchom następujące polecenie w oknie wiersza polecenia programu Visual Studio.
cl.exe /EHsc task-tree-search.cpp
Zobacz też
Anulowanie w PPL
Obsługa wyjątków
Równoległość zadań
Algorytmy równoległe
task_group, klasa
structured_task_group, klasa
parallel_for_each, funkcja