작업 병렬 처리(동시성 런타임)
업데이트: 2011년 3월
이 문서에서는 동시성 런타임에서 작업 및 작업 그룹의 역할에 대해 설명합니다. 동시에 실행할 두 개 이상의 독립적인 작업 항목이 있는 경우 작업 그룹을 사용합니다. 예를 들어 남은 작업을 두 개의 파티션으로 나누는 재귀 알고리즘이 있다고 가정해 봅니다. 작업 그룹을 사용하여 이러한 파티션을 동시에 실행할 수 있습니다. 반대로 컬렉션의 각 요소에 동일한 루틴을 병렬로 적용하려는 경우에는 Concurrency::parallel_for와 같은 병렬 알고리즘을 사용합니다. 병렬 알고리즘에 대한 자세한 내용은 병렬 알고리즘을 참조하십시오.
작업 및 작업 그룹
작업(task)은 특정 업무를 수행하는 작업(work) 단위입니다. 일반적으로 작업을 다른 작업과 병렬로 실행할 수 있으며 작업을 더 세분화된 추가 작업으로 분해할 수 있습니다. 작업 그룹은 작업 컬렉션을 구성합니다. 작업 그룹은 작업 가로채기 큐에 작업을 넣습니다. 스케줄러는 이 큐에서 작업을 제거하고 사용 가능한 컴퓨팅 리소스에서 해당 작업을 실행합니다. 작업 그룹에 작업을 추가한 후 모든 작업이 끝날 때까지 기다리거나 아직 시작되지 않은 작업을 취소할 수 있습니다.
PPL은 Concurrency::task_group 및 Concurrency::structured_task_group 클래스를 사용하여 작업 그룹을 나타내고 Concurrency::task_handle 클래스를 사용하여 작업을 나타냅니다. task_handle 클래스는 작업을 수행하는 코드를 캡슐화합니다. 이 코드는 람다 함수, 람다 포인터 또는 함수 개체의 형태로 제공되며 작업 함수라고도 합니다. 일반적으로 task_handle 개체를 직접 사용할 필요는 없습니다. 대신 작업 함수를 작업 그룹에 전달하면 작업 그룹은 task_handle 개체를 만들고 관리합니다.
PPL은 작업 그룹을 비구조적 작업 그룹 및 구조적 작업 그룹이라는 두 개의 범주로 분할합니다. PPL은 task_group 클래스를 사용하여 비구조적 작업 그룹을 나타내고 structured_task_group 클래스를 사용하여 구조적 작업 그룹을 나타냅니다.
중요
PPL은 structured_task_group 클래스를 사용하여 작업 집합을 병렬로 실행하는 Concurrency::parallel_invoke 알고리즘도 정의합니다. parallel_invoke 알고리즘의 구문이 더 간결하므로 가능하면 structured_task_group 클래스 대신 이 알고리즘을 사용하는 것이 좋습니다. 병렬 알고리즘 항목에서 parallel_invoke에 대해 더 자세히 설명합니다.
동시에 실행할 독립적인 작업이 여러 개 있고 계속하지 않고 모든 작업이 끝날 때까지 기다려야 하는 경우 parallel_invoke를 사용합니다. 동시에 실행할 독립적인 작업이 여러 개 있지만 나중에 작업이 끝날 때까지 기다리려는 경우 task_group을 사용합니다. 예를 들어 task_group개체에 작업을 추가하고 다른 함수 또는 다른 스레드에서 작업이 끝날 때까지 기다릴 수 있습니다.
작업 그룹에서는 취소 개념을 지원합니다. 취소를 사용하면 전체 작업(operation)을 취소하려는 모든 활성 작업(task)에 신호를 보낼 수 있습니다. 취소를 사용하면 아직 시작되지 않은 작업을 시작되지 않게 할 수도 있습니다. 취소에 대한 자세한 내용은 PPL에서의 취소를 참조하십시오.
런타임에서는 관련된 작업 그룹이 끝나기를 기다릴 때 작업에서 예외를 throw하고 해당 예외를 처리할 수 있게 하는 예외 처리 모델을 제공합니다. 예외 처리 모델에 대한 자세한 내용은 동시성 런타임에서 예외 처리를 참조하십시오.
task_group을 structured_task_group과 비교
structured_task_group 클래스 대신 task_group 또는 parallel_invoke를 사용하는 것이 좋지만, 예를 들어 여러 가지 작업을 수행하거나 취소에 대한 지원이 필요한 병렬 알고리즘을 작성할 때 structured_task_group을 사용하는 경우도 있습니다. 이 단원에서는 task_group 클래스와 structured_task_group 클래스의 차이점에 대해 설명합니다.
task_group 클래스는 스레드로부터 안전합니다. 따라서 여러 스레드의 task_group 개체에 작업을 추가하고 여러 스레드의 task_group 개체를 기다리거나 취소할 수 있습니다. structured_task_group 개체의 생성과 소멸은 동일한 어휘 범위에서 발생해야 합니다. 또한 structured_task_group 개체에 대한 모든 작업은 동일한 스레드에서 발생해야 합니다. 이 규칙에 대한 예외는 Concurrency::structured_task_group::cancel 및 Concurrency::structured_task_group::is_canceling 메서드입니다. 자식 작업은 언제든지 이러한 메서드를 호출하여 부모 작업 그룹을 취소하거나 취소를 확인할 수 있습니다.
Concurrency::task_group::wait 또는 Concurrency::task_group::run_and_wait 메서드를 호출한 후 task_group 개체에 대한 추가 작업을 실행할 수 있습니다. 반대로 Concurrency::structured_task_group::wait or Concurrency:: structured_task_group::run_and_wait 메서드를 호출한 후에는 structured_task_group에 대한 추가 작업을 실행할 수 없습니다.
structured_task_group 클래스는 스레드 간에 동기화하지 않으므로 task_group 클래스보다 실행 오버헤드가 적습니다. 따라서 여러 스레드에서 작업을 예약할 필요가 없고 parallel_invoke 알고리즘을 사용할 수 없는 경우 structured_task_group 클래스를 사용하면 성능이 높은 코드를 작성할 수 있습니다.
다른 structured_task_group 개체 내에 있는 structured_task_group 개체를 사용하는 외부 개체를 완료하기 전에 먼저 내부 개체를 완료하고 소멸시켜야 합니다. task_group 클래스를 사용하는 경우에는 외부 그룹을 완료하기 전에 중첩된 작업 그룹을 완료할 필요가 없습니다.
비구조적 작업 그룹과 구조적 작업 그룹은 다양한 방식으로 작업 핸들을 사용합니다. 작업 함수를 task_group 개체에 직접 전달할 수 있으며 그러면 task_group 개체에서 자동으로 작업을 만들고 관리합니다. structured_task_group 클래스를 사용하려면 각 작업의 task_handle 개체를 관리해야 합니다. 관련된 structured_task_group 개체의 수명이 지속되는 동안 모든 task_handle 개체가 유효한 상태로 유지되어야 합니다. 다음 기본 예제에서와 같이 Concurrency::make_task 함수를 사용하여 task_handle 개체를 만들 수 있습니다.
// make-task-structure.cpp
// compile with: /EHsc
#include <ppl.h>
using namespace Concurrency;
int wmain()
{
// Use the make_task function to define several tasks.
auto task1 = make_task([] { /*TODO: Define the task body.*/ });
auto task2 = make_task([] { /*TODO: Define the task body.*/ });
auto task3 = make_task([] { /*TODO: Define the task body.*/ });
// Create a structured task group and run the tasks concurrently.
structured_task_group tasks;
tasks.run(task1);
tasks.run(task2);
tasks.run_and_wait(task3);
}
여러 가지 작업이 있는 경우에 사용할 작업 핸들을 관리하려면 _malloca와 같은 스택 할당 루틴 또는 std::vector와 같은 컨테이너 클래스를 사용합니다.
task_group과 structured_task_group 모두 취소 기능을 지원합니다. 취소에 대한 자세한 내용은 PPL에서의 취소를 참조하십시오.
예제
다음 기본 예제에서는 작업 그룹을 사용하는 방법을 보여 줍니다. 이 예제에서는 parallel_invoke 알고리즘을 사용하여 두 작업을 병렬로 수행합니다. 각 작업은 하위 작업을 task_group 개체에 추가합니다. task_group 클래스를 사용하면 여러 작업이 이 개체에 작업을 동시에 추가할 수 있습니다.
// using-task-groups.cpp
// compile with: /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>
using namespace Concurrency;
using namespace std;
// Prints a message to the console.
template<typename T>
void print_message(T t)
{
wstringstream ss;
ss << L"Message from task: " << t << endl;
wcout << ss.str();
}
int wmain()
{
// A task_group object that can be used from multiple threads.
task_group tasks;
// Concurrently add several tasks to the task_group object.
parallel_invoke(
[&] {
// Add a few tasks to the task_group object.
tasks.run([] { print_message(L"Hello"); });
tasks.run([] { print_message(42); });
},
[&] {
// Add one additional task to the task_group object.
tasks.run([] { print_message(3.14); });
}
);
// Wait for all tasks to finish.
tasks.wait();
}
이 예제를 실행하면 다음과 같은 샘플 결과가 출력됩니다.
Message from task: Hello
Message from task: 3.14
Message from task: 42
parallel_invoke 알고리즘은 작업을 동시에 실행하므로 출력 메시지의 순서가 다를 수 있습니다.
parallel_invoke 알고리즘을 사용하는 방법을 보여 주는 전체 예제를 보려면 방법: parallel_invoke를 사용하여 병렬 정렬 루틴 작성 및 방법: parallel_invoke를 사용하여 병렬 작업 실행을 참조하십시오. task_group 클래스를 사용하여 비동기 미래를 구현하는 전체 예제를 보려면 연습: 미래 구현을 참조하십시오.
강력한 프로그래밍
작업 그룹과 병렬 알고리즘을 사용할 때는 취소 및 예외 처리의 역할을 이해하고 있어야 합니다. 예를 들어 병렬 작업 트리에서 취소된 작업으로 인해 자식 작업이 실행되지 않게 됩니다. 자식 작업 중 하나가 사용자의 응용 프로그램에 중요한 작업(예: 리소스 해제)을 수행하는 경우 이로 인해 문제가 발생할 수 있습니다. 또한 자식 작업에서 예외를 throw하면 해당 예외가 개체 소멸자를 통해 전파되고 응용 프로그램에서 정의되지 않은 동작이 발생할 수 있습니다. 이러한 지점을 보여 주는 예제를 보려면 병렬 패턴 라이브러리의 유용한 정보 문서에 있는 취소 및 예외 처리가 개체 소멸에 미치는 영향 이해 단원을 참조하십시오. PPL의 취소 및 예외 처리 모델에 대한 자세한 내용은 PPL에서의 취소 및 동시성 런타임에서 예외 처리를 참조하십시오.
관련 항목
방법: parallel_invoke를 사용하여 병렬 정렬 루틴 작성
parallel_invoke 알고리즘을 사용하여 바이토닉 정렬 알고리즘의 성능을 향상시키는 방법을 보여 줍니다.방법: parallel_invoke를 사용하여 병렬 작업 실행
parallel_invoke 알고리즘을 사용하여 공유 데이터 소스에 대해 여러 작업을 수행하는 프로그램의 성능을 향상시키는 방법을 보여 줍니다.연습: 미래 구현
동시성 런타임의 기존 기능을 결합하여 더 많은 작업을 수행하는 방법을 보여 줍니다.PPL(병렬 패턴 라이브러리)
동시 응용 프로그램 개발을 위해 명령형 프로그래밍 모델을 제공하는 PPL에 대해 설명합니다.
참조
변경 기록
날짜 |
변경 내용 |
이유 |
---|---|---|
2011년 3월 |
작업 그룹과 병렬 알고리즘을 사용하는 경우 취소 및 예외 처리의 역할에 대한 정보를 추가했습니다. |
향상된 기능 관련 정보 |
2010년 7월 |
내용을 다시 구성했습니다. |
향상된 기능 관련 정보 |
2010년 5월 |
지침을 확장했습니다. |
향상된 기능 관련 정보 |