Share via


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

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

토픽 취소 는 병렬 패턴 라이브러리에서 취소의 역할을 설명합니다. 예외 처리의 사용은 동시성::task_group::cancel동시성::structured_task_group::cancel 메서드를 사용하는 것보다 병렬 작업을 취소하는 덜 효율적인 방법입니다. 그러나 작업 또는 병렬 알고리즘을 사용하지만 취소할 개체를 structured_task_group 제공하지 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::p arallel_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(begin(_children), end(_children), [&](tree& child) {
      child.for_all(action);
   });

   // Perform the action on this node.
   action(*this);
}

예: 트리에서 값을 검색합니다.

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

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

작업 그룹에 제공하는 작업 함수가 예외를 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); }
   );
}

이 예제에서는 동시성::p arallel_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(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); }
   );
}

이 예제에서는 다음 샘플 출력을 생성합니다.

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 명령 프롬프트 창에서 다음 명령을 실행합니다.

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

참고 항목

PPL에서의 취소
예외 처리
작업 병렬 처리
병렬 알고리즘
task_group 클래스
structured_task_group 클래스
parallel_for_each 함수