Поделиться через


Практическое руководство. Использование обработки исключений для выхода из параллельного цикла

В этом разделе описано написание алгоритма поиска в базовой древовидной структуре.

В разделе Отмена в библиотеке параллельных шаблонов объясняется роль отмены в библиотеке параллельных шаблонов. Обработка исключений является менее эффективным способом отмены параллельной работы, чем методы Concurrency::task_group::cancel и Concurrency::structured_task_group::cancel. Один из сценариев, в которых можно использовать обработку исключений для отмены работы — вызов в библиотеку стороннего производителя, использующую задачи или параллельные алгоритмы, но не предоставляющую для отмены объект task_group или structured_task_group.

Пример

В следующем примере показан базовый тип tree, который содержит элемент данных и список дочерних узлов. В следующем разделе показано тело метода for_all, который рекурсивно выполняет рабочую функцию на каждом дочернем узле.

// 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;
};

В следующем примере показан метод for_all. В этом методе алгоритм Concurrency::parallel_for_each используется для параллельного выполнения рабочей функции на каждом узле дерева.

// 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);
}

В следующем примере показана функция search_for_value, осуществляющая поиск значения в предоставленном объекте tree. Эта функция передает методу for_all рабочую функцию, которая создает исключение при обнаружении узла дерева, содержащего предоставленное значение.

Предположим, что класс tree предоставлен библиотекой стороннего производителя и его нельзя изменять. В этом случае уместно использование обработки исключений, так как метод for_all не предоставляет вызывающему объекту объект task_group или structured_task_group. Следовательно, рабочей функции не удается непосредственно отменить свою родительскую группу задач.

Если предоставленная группе задач рабочая функция создает исключение, среда выполнения прекращает выполнение всех задач группы задач (включая все дочерние группы задач) и удаляет все задачи, выполнение которых еще не началось. Функция search_for_value использует блок try-catch, чтобы зафиксировать исключение и вывести результат на консоль.

// 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();   
}

В следующем примере создается объект tree и выполняется параллельный поиск нескольких значений в этом объекте. Использование функции build_tree показано далее в этом разделе.

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); }
   );
}

В этом примере алгоритм Concurrency::parallel_invoke используется для параллельного поиска значений. Дополнительные сведения об этом алгоритме см. в разделе Параллельные алгоритмы.

В следующем полном примере обработка исключений используется для поиска значений в базовой древовидной структуре.

// 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); }
   );
}

В данном примере получается следующий результат.

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 2010 следующую команду.

cl.exe /EHsc task-tree-search.cpp

См. также

Ссылки

Класс task_group

Класс structured_task_group

Функция parallel_for_each

Основные понятия

Отмена в библиотеке параллельных шаблонов

Обработка исключений в среде выполнения с параллелизмом

Параллелизм задач (среда выполнения с параллелизмом)

Параллельные алгоритмы

Журнал изменений

Дата

Журнал

Причина

Март 2011

Объяснено использование обработки исключений для отмены параллельной операции.

Обратная связь от клиента.