방법: 예외 처리를 사용하여 병렬 루프 중단
업데이트: 2011년 3월
이 항목에서는 기본 트리 구조에 대한 검색 알고리즘을 작성하는 방법을 보여 줍니다.
PPL에서의 취소 항목에서는 병렬 패턴 라이브러리에서 취소의 역할에 대해 설명합니다. 병렬 작업을 취소할 때 예외 처리를 사용하는 것이 Concurrency::task_group::cancel 및 Concurrency::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
참고 항목
참조
개념
변경 기록
날짜 |
변경 내용 |
이유 |
---|---|---|
2011년 3월 |
예외 처리를 사용하여 병렬 작업을 취소하는 경우를 명확하게 설명했습니다. |
고객 의견 |