如何:使用异常处理中断 Parallel 循环

更新:2011 年 3 月

本主题演示如何编写基本树结构的搜索算法。

PPL 中的取消操作主题说明了并行模式库中的取消功能。 使用异常处理取消并行工作不如使用 Concurrency::task_group::cancelConcurrency::structured_task_group::cancel 方法有效。 但是,如果您调用使用任务或并行算法但不提供 task_groupstructured_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_groupstructured_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 函数

概念

PPL 中的取消操作

并发运行时中的异常处理

任务并行(并发运行时)

并行算法

修订记录

日期

修订记录

原因

2011 年 3 月

说明了何时使用异常处理取消并行操作。

客户反馈