Cómo: Usar el control de excepciones para interrumpir un bucle Parallel
En este tema se muestra cómo escribir un algoritmo de búsqueda para una estructura de árbol básica.
Cancelación en la biblioteca PPL explica el rol de cancelación en la Biblioteca de modelos de procesamiento paralelo (PPL).El uso del control de excepciones es una manera menos eficaz de cancelar el trabajo paralelo que el uso de los métodos de concurrency::task_group::cancel y de concurrency::structured_task_group::cancel.Sin embargo, un escenario donde es apropiado el uso del control de excepciones para cancelar trabajo es cuando se llama a una biblioteca de terceros que use tareas o algoritmos paralelos pero no proporcione un objeto task_group o structured_task_group para la cancelación.
Ejemplo
En el siguiente ejemplo se muestra un tipo tree básico que contiene un elemento de datos y una lista de nodos secundarios.En la siguiente sección se muestra el cuerpo del método for_all, que realiza una función de trabajo en cada nodo secundario de forma recursiva.
// 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;
};
En el ejemplo siguiente se muestra el método for_all.Usa el algoritmo de concurrency::parallel_for_each para realizar una función de trabajo en cada nodo del árbol en paralelo.
// 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);
}
En el siguiente ejemplo se muestra la función search_for_value, que busca un valor en el objeto tree proporcionado.Esta función pasa al método for_all una función de trabajo que se inicia cuando encuentra un nodo de árbol que contiene el valor proporcionado.
Suponga que la clase tree está proporcionada por una biblioteca de terceros y que no puede modificarla.En este caso, el uso del control de excepciones es adecuado porque el método for_all no proporciona un objeto task_group o structured_task_group al llamador.Por consiguiente, la función de trabajo no puede cancelar su grupo de tareas primario directamente.
Cuando la función de trabajo que se proporciona a un grupo de tareas produce una excepción, el runtime detiene todas las tareas incluidas en el grupo de tareas (y cualquier grupo de tareas secundario) y descarta cualquier tarea que no se haya iniciado todavía.La función search_for_value usa un bloque try-catch para capturar la excepción e imprimir el resultado en la consola.
// 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();
}
En el siguiente ejemplo se crea un objeto tree y se buscan en él varios valores en paralelo.La función build_tree se muestra más adelante en este tema.
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); }
);
}
Este ejemplo utiliza el algoritmo de concurrency::parallel_invoke para buscar valores en paralelo.Para obtener más información sobre este algoritmo, vea Algoritmos paralelos.
En el siguiente ejemplo completo se usa el control de excepciones para buscar valores en una estructura de árbol básica.
// 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); }
);
}
Este ejemplo genera la siguiente salida de ejemplo.
Compilar el código
Copie el código de ejemplo y péguelo en un proyecto de Visual Studio, o péguelo en un archivo denominado task-tree-search.cpp y después se ejecute el siguiente comando en una ventana de símbolo del sistema de Visual Studio.
cl.exe /EHsc task-tree-search.cpp
Vea también
Referencia
Conceptos
Cancelación en la biblioteca PPL
Control de excepciones en el runtime de simultaneidad