다음을 통해 공유


방법: 예외 처리를 사용하여 병렬 루프 중단

업데이트: 2011년 3월

이 항목에서는 기본 트리 구조에 대한 검색 알고리즘을 작성하는 방법을 보여 줍니다.

PPL에서의 취소 항목에서는 병렬 패턴 라이브러리에서 취소의 역할에 대해 설명합니다. 병렬 작업을 취소할 때 예외 처리를 사용하는 것이 Concurrency::task_group::cancelConcurrency::structured_task_group::cancel 메서드를 사용하는 것보다 효율성이 떨어지는 방법입니다. 그러나 작업(task) 또는 병렬 알고리즘을 사용하지만 취소할 task_group 또는 structured_task_group 개체를 제공하지 않는 타사 라이브러리를 호출하는 경우에는 예외 처리를 사용하여 작업(work)을 취소하는 것이 적절합니다.

예제

다음 예제에서는 데이터 요소 및 자식 노드 목록을 포함하는 기본 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);
}

다음 예제에서는 제공된 tree 개체에서 값을 검색하는 search_for_value 함수를 보여 줍니다. 이 함수는 제공된 값을 포함하는 트리 노드를 발견하면 throw하는 작업 함수를 for_all 메서드에 전달합니다.

타사 라이브러리에서 tree 클래스가 제공되고 이 클래스를 수정할 수 없다고 가정합니다. 이 경우에는 for_all 메서드가 task_group 또는 structured_task_group 개체를 호출자에게 제공하지 않으므로 예외 처리를 사용하는 것이 적절합니다. 따라서 작업 함수에서 부모 작업 그룹을 직접 취소할 수 없습니다.

작업 그룹에 제공하는 작업 함수에서 예외를 throw하면 런타임에서 자식 작업 그룹을 포함하여 작업 그룹에 있는 모든 작업을 중지하고 아직 시작되지 않은 작업을 취소합니다. 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월

예외 처리를 사용하여 병렬 작업을 취소하는 경우를 명확하게 설명했습니다.

고객 의견