Como: Use o tratamento de exceção para quebra de um Loop paralelo
Este tópico mostra como escrever um algoritmo de pesquisa para uma estrutura de árvore básico.
O tópico Cancelamento na PPL explica a função de cancelamento na biblioteca de padrões paralelos. O uso de manipulação de exceção é cancelar o trabalho paralelo que o uso de uma maneira menos eficiente do Concurrency::task_group::cancel e Concurrency::structured_task_group::cancel métodos. No entanto, um cenário onde o uso de manipulação de exceção para cancelar o trabalho é apropriado é quando você chama em uma biblioteca de terceiros que usa algoritmos paralelos ou tarefas, mas não fornece um task_group ou structured_task_group o objeto para cancelar.
Exemplo
O exemplo a seguir mostra um basic tree tipo que contém um elemento de dados e uma lista de nós do filho. A seção a seguir mostra o corpo da for_all método, o qual recursivamente executa uma função de trabalho em cada nó filho.
// 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;
};
A exemplo a seguir mostra a for_all método. Ele usa o Concurrency::parallel_for_each o algoritmo para realizar uma função de trabalho em cada nó da árvore em 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(_children.begin(), _children.end(), [&](tree& child) {
child.for_all(action);
});
// Perform the action on this node.
action(*this);
}
A exemplo a seguir mostra a search_for_value a função, que procura um valor em fornecida tree objeto. Esta função passa para o for_all método uma função de trabalho que lança quando ele localiza um nó de árvore que contém o valor fornecido.
Suponha que o tree classe é fornecida por uma biblioteca de terceiros, e que você não pode modificar o proprietário. Nesse caso, o uso de manipulação de exceção é apropriado porque o for_all método não fornece um task_group ou structured_task_group o objeto para o chamador. Portanto, a função de trabalho é não é possível cancelar diretamente de seu grupo de tarefas do pai.
Quando a função de trabalho que você fornecer para um grupo de tarefas lança uma exceção, o tempo de execução interrompe todas as tarefas que estão no grupo de tarefas (incluindo quaisquer grupos de tarefas filho) e descarta quaisquer tarefas que ainda não iniciaram. O search_for_value função usa um try–catch bloco para capturar a exceção e imprimir o resultado no console.
// 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();
}
O exemplo a seguir cria um tree de objeto e a procura por diversos valores em paralelo. O build_tree função é mostrada posteriormente neste tópico.
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 exemplo usa a Concurrency::parallel_invoke o algoritmo para pesquisar valores em paralelo. Para obter mais informações sobre esse algoritmo, consulte Algoritmos paralelos.
O exemplo a seguir completo usa o tratamento de exceção para pesquisar valores em uma estrutura de árvore básico.
// 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); }
);
}
Este exemplo produz a saída de exemplo a seguir.
Found a node with value 32614.
Found a node with value 86131.
Did not find node with value 17522.
Compilando o código
Copie o código de exemplo e colá-lo em um Visual Studio do projeto, ou colá-lo em um arquivo que é chamado search.cpp de árvore de tarefa e, em seguida, execute o seguinte comando um Visual Studio 2010 janela do Prompt de comando.
cl.exe /EHsc task-tree-search.cpp
Consulte também
Referência
Conceitos
O Runtime de simultaneidade de manipulação de exceção
Paralelismo de tarefas (Runtime de simultaneidade)
Histórico de alterações
Date |
History |
Motivo |
---|---|---|
Março de 2011 |
Esclarecida quando usar manipulação de exceção para cancelar uma operação em paralelo. |
Comentários do cliente. |