Практическое руководство. Использование обработки исключений для выхода из параллельного цикла
Мақала
В этом разделе показано, как написать алгоритм поиска для базовой структуры дерева.
В разделе "Отмена" объясняется роль отмены в библиотеке параллельных шаблонов. Использование обработки исключений является менее эффективным способом отмены параллельной работы, чем использование методов параллелизма::task_group::cancel и concurrency::structured_task_group::cancel . Однако один из сценариев, когда использование обработки исключений для отмены работы подходит при вызове в стороннюю библиотеку, которая использует задачи или параллельные алгоритмы, но не предоставляет task_groupstructured_task_group объект для отмены.
Пример: базовый тип дерева
В следующем примере показан базовый tree тип, содержащий элемент данных и список дочерних узлов. В следующем разделе показан текст for_all метода, который рекурсивно выполняет рабочую функцию на каждом дочернем узле.
C++
// A simple tree structure that has multiple child nodes.template <typename T>
classtree
{public:
explicittree(T data)
: _data(data){
}
// Retrieves the data element for the node. T get_data()const{
return _data;
}
// Adds a child node to the tree.voidadd_child(tree& child){
_children.push_back(child);
}
// Performs the given work function on the data element of the tree and// on each child.template<classFunction>
voidfor_all(Function& action);private:
// The data for this node.
T _data;
// The child nodes.list<tree> _children;
};
Пример. Параллельное выполнение работы
В следующем примере показан for_all метод. Он использует алгоритм параллелизма::p arallel_for_each для выполнения рабочей функции на каждом узле дерева параллельно.
C++
// Performs the given work function on the data element of the tree and// on each child.template<classFunction>
voidfor_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);
}
Пример. Поиск дерева для значения
В следующем примере показана функция search_for_value, которая выполняет поиск значения в предоставленном объекте tree. Эта функция передает for_all методу рабочую функцию, которая вызывается при поиске узла дерева, содержащего предоставленное значение.
Предположим, что tree класс предоставляется сторонней библиотекой и что ее нельзя изменить. В этом случае использование обработки исключений подходит, так как for_all метод не предоставляет task_group вызывающим объекту или structured_task_group объекту. Поэтому рабочая функция не может напрямую отменить ее родительскую группу задач.
Когда рабочая функция, предоставляемая группе задач, создает исключение, среда выполнения останавливает все задачи, которые находятся в группе задач (включая все дочерние группы задач), и удаляет все задачи, которые еще не запущены. Функция search_for_value использует блок try-catch, чтобы зафиксировать исключение и вывести результат на консоль.
C++
// Searches for a value in the provided tree object.template <typename T>
voidsearch_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();
}
Пример. Создание и поиск дерева параллельно
В следующем примере создается объект и выполняется поиск нескольких значений tree параллельно. Эта build_tree функция показана далее в этом разделе.
C++
intwmain(){
// 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); }
);
}
Следующий полный пример использует обработку исключений для поиска значений в базовой структуре дерева.
C++
// task-tree-search.cpp// compile with: /EHsc#include<ppl.h>#include<list>#include<iostream>#include<algorithm>#include<sstream>#include<random>usingnamespace concurrency;
usingnamespacestd;
// A simple tree structure that has multiple child nodes.template <typename T>
classtree
{public:
explicittree(T data)
: _data(data){
}
// Retrieves the data element for the node. T get_data()const{
return _data;
}
// Adds a child node to the tree.voidadd_child(tree& child){
_children.push_back(child);
}
// Performs the given work function on the data element of the tree and// on each child.template<classFunction>
voidfor_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, classGenerator>
tree<T> build_tree(intdepth, intchild_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>
voidsearch_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();
}
intwmain(){
// 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); }
);
}
В этом примере создается следующий пример выходных данных.
Output
Found a node with value 32614.
Found a node with value 86131.
Did not find node with value 17522.
Компиляция кода
Скопируйте пример кода и вставьте его в проект Visual Studio или вставьте его в файл с именем task-tree-search.cpp , а затем выполните следующую команду в окне командной строки Visual Studio.
В этом модуле рассматривается использование исключений и процесса обработки исключений в консольных приложениях C#. Практические действия обеспечивают реализацию шаблонов обработки исключений для различных сценариев программирования.