다음을 통해 공유


동시성 런타임에서 예외 처리

동시성 런타임에서는 C++ 예외 처리를 사용하여 다양한 유형의 오류를 이해합니다.이러한 오류 런타임, 리소스를 얻기 위해 오류와 같은 런타임 오류가 발생 하는 오류 잘못 된 사용 작업 및 작업 그룹에 제공 하는 작업 함수에 포함 됩니다.작업 또는 작업 그룹에서 예외를 throw 하는 경우 런타임에서 해당 예외를 보유 하 고 대기 작업 또는 완료 하는 작업 그룹에 대 한 컨텍스트로 마샬링합니다.간단한 작업 및 에이전트와 같은 구성 요소의 경우 런타임에서는 예외를 자동으로 관리하지 않습니다.이러한 경우에는 사용자 고유의 예외 처리 메커니즘을 구현해야 합니다.이 항목 런타임 작업, 작업 그룹, 간단한 작업 및 비동기 에이전트에서 throw 된 예외를 처리 하는 방법을 응용 프로그램에서 예외에 응답 하는 방법을 설명 합니다.

주요 사항

  • 작업 또는 작업 그룹에서 예외를 throw 하는 경우 런타임에서 해당 예외를 보유 하 고 대기 작업 또는 완료 하는 작업 그룹에 대 한 컨텍스트로 마샬링합니다.

  • 가능 하면 모든 호출을 둘러싸는 concurrency::task::getconcurrency::task::wait 에 try/catch 에서 복구할 수 있는 오류를 처리 하는 블록.작업에서 예외를 throw 한 작업에서 해당 연속 문자 또는 기본 응용 프로그램 중 하나 예외를 catch 되지 않는 경우 런타임 응용 프로그램을 종료 합니다.

  • 작업 기반 연속 항상 실행 됩니다. 선행 작업이 성공적으로 완료가 예외를 throw 하거나 취소 되었는지 여부를 중요 하지 않습니다.선행 작업에서 throw 되거나 취소 된 경우에 값에 따라 연속 실행 되지 않습니다.

  • 작업 기반 연속은 항상 실행 되므로 작업 기반 연속에 연속 체인의 끝에 추가할 고려해 야 합니다.이 코드는 모든 예외를 준수를 보장 하는 데 도움이 됩니다.

  • 런타임에서 throw concurrency::task_canceled 를 호출 하면 concurrency::task::get 해당 작업이 취소 됩니다.

  • 런타임에 간단한 작업 및 에이전트에 대 한 예외를 관리 하지 않습니다.

이 문서에서

  • 작업 및 연속

  • 작업 그룹 및 병렬 알고리즘

  • 런타임에서 Throw되는 예외

  • 여러 예외

  • 취소

  • 간단한 작업

  • 비동기 에이전트

작업 및 연속

런타임에 throw 되는 예외를 처리 하는 방법을 설명 concurrency::task 개체와 해당 연속 문자입니다.작업 및 연속 모델에 대 한 자세한 내용은 작업 병렬 처리(동시성 런타임).

작업을 전달 하는 함수 본문에서 예외가 throw 한 task 개체에서 런타임에 예외를 저장 하 고 호출 컨텍스트를 마샬링하 concurrency::task::get 또는 concurrency::task::wait.문서 작업 병렬 처리(동시성 런타임) 설명 값을 기준으로 연속 작업 기반 및 요약할 수 있지만 값을 기준으로 연속으로 형식 매개 변수를 사용 T 및 형식 매개 변수를 사용 하는 작업 기반 연속 task<T>.Throw 작업 하나 이상의 값에 따라 연속 된 경우 이러한 연속 실행 되도록 예약 됩니다.다음 예제는 이 동작을 보여 줍니다.

// eh-task.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    wcout << L"Running a task..." << endl;
    // Create a task that throws.
    auto t = create_task([]
    {
        throw exception();
    });

    // Create a continuation that prints its input value.
    auto continuation = t.then([]
    {
        // We do not expect this task to run because
        // the antecedent task threw.
        wcout << L"In continuation task..." << endl;
    });

    // Wait for the continuation to finish and handle any 
    // error that occurs.
    try
    {
        wcout << L"Waiting for tasks to finish..." << endl;
        continuation.wait();

        // Alternatively, call get() to produce the same result.
        //continuation.get();
    }
    catch (const exception& e)
    {
        wcout << L"Caught exception." << endl;
    }
}
/* Output:
    Running a task...
    Waiting for tasks to finish...
    Caught exception.
*/

작업 기반 연속 선행 작업에서 throw 되는 예외를 처리할 수 있습니다.작업 기반 연속 항상 실행 됩니다. 작업이 성공적으로 완료가 예외를 throw 하거나 취소 여부를 중요 하지 않습니다.작업에서 예외를 throw 하는 경우 해당 작업 기반 연속 실행 되도록 예약 됩니다.다음 예제에서는 항상 throw 작업을 보여 줍니다.작업은 두 개의 연속 된 있습니다. 하나 값 기반 이며 다른 작업 기반입니다.작업 기반 예외 항상 실행 되 고 따라서 선행 작업에서 throw 되는 예외를 catch 할 수 있습니다.두 연속은 완료 하는 대기 하는 경우 작업 예외를 항상 throw 하기 때문에 예외가 다시 throw 됩니다 task::get 또는 task::wait 라고 합니다.

// eh-continuations.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{    
    wcout << L"Running a task..." << endl;
    // Create a task that throws.
    auto t = create_task([]() -> int
    {
        throw exception();
        return 42;
    });

    //
    // Attach two continuations to the task. The first continuation is  
    // value-based; the second is task-based.

    // Value-based continuation.
    auto c1 = t.then([](int n)
    {
        // We don't expect to get here because the antecedent 
        // task always throws.
        wcout << L"Received " << n << L'.' << endl;
    });

    // Task-based continuation.
    auto c2 = t.then([](task<int> previousTask)
    {
        // We do expect to get here because task-based continuations
        // are scheduled even when the antecedent task throws.
        try
        {
            wcout << L"Received " << previousTask.get() << L'.' << endl;
        }
        catch (const exception& e)
        {
            wcout << L"Caught exception from previous task." << endl;
        }
    });

    // Wait for the continuations to finish.
    try
    {
        wcout << L"Waiting for tasks to finish..." << endl;
        (c1 && c2).wait();
    }
    catch (const exception& e)
    {
        wcout << L"Caught exception while waiting for all tasks to finish." << endl;
    }
}
/* Output:
    Running a task...
    Waiting for tasks to finish...
    Caught exception from previous task.
    Caught exception while waiting for all tasks to finish.
*/

처리할 수 있는 예외를 catch 하려면 연속 작업 기반 사용 하는 것이 좋습니다.작업 기반 연속은 항상 실행 되므로 작업 기반 연속에 연속 체인의 끝에 추가할 고려해 야 합니다.이 코드는 모든 예외를 준수를 보장 하는 데 도움이 됩니다.다음 예제에서는 기본 값에 따라 연속 체인을 보여 줍니다.세 번째 작업 체인에서 throw, 및 따라서 그 뒤에 나오는 값에 따라 연속은 실행 되지 않습니다.그러나 최종 연속 작업 기반 이며 따라서 항상 실행 됩니다.이 마지막 연속 세 번째 작업에서 throw 되는 예외를 처리 합니다.

우리가 할 수 있는 가장 구체적인 예외를 catch 하는 것이 좋습니다.특정 예외를 catch 할 수 없는 경우이 최종 작업 기반 연속을 생략할 수 있습니다.모든 예외는 처리 되지 않은 상태로 유지 됩니다 및 응용 프로그램을 종료할 수 있습니다.

// eh-task-chain.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
    int n = 1;
    create_task([n]
    {
        wcout << L"In first task. n = ";
        wcout << n << endl;

        return n * 2;

    }).then([](int n)
    {
        wcout << L"In second task. n = ";
        wcout << n << endl;

        return n * 2;

    }).then([](int n)
    {
        wcout << L"In third task. n = ";
        wcout << n << endl;

        // This task throws.
        throw exception();
        // Not reached.
        return n * 2;

    }).then([](int n)
    {
        // This continuation is not run because the previous task throws.
        wcout << L"In fourth task. n = ";
        wcout << n << endl;

        return n * 2;

    }).then([](task<int> previousTask)
    {
        // This continuation is run because it is value-based.
        try
        {
            // The call to task::get rethrows the exception.
            wcout << L"In final task. result = ";
            wcout << previousTask.get() << endl;
        }
        catch (const exception&)
        {
            wcout << L"<exception>" << endl;
        }
    }).wait();
}
/* Output:
    In first task. n = 1
    In second task. n = 2
    In third task. n = 4
    In final task. result = <exception>
*/
팁

사용할 수 있는 concurrency::task_completion_event::set_exception 예외가 작업 완료 이벤트를 연결 하는 메서드.문서 작업 병렬 처리(동시성 런타임) 설명 된 concurrency::task_completion_event 클래스에 대해서는.

concurrency::task_canceled 에 관련 된 중요 한 런타임 예외 형식 인지 task.런타임에서 throw task_canceled 를 호출 하면 task::get 해당 작업이 취소 됩니다.(반대로 task::wait 반환 task_status::canceled 및 throw 하지 않습니다.) Catch 하 고 작업 기반 연속 또는 호출 하는 경우이 예외를 처리할 수 있는 task::get.작업 취소에 대 한 자세한 내용은 PPL에서의 취소.

주의 정보주의

Throw task_canceled 코드에서.호출 concurrency::cancel_current_task 대신 합니다.

작업에서 예외를 throw 한 작업에서 해당 연속 문자 또는 기본 응용 프로그램 중 하나 예외를 catch 되지 않는 경우 런타임 응용 프로그램을 종료 합니다.응용 프로그램 충돌이 발생 하면 Visual Studio c + + 예외가 throw 되 면 침입을 구성할 수 있습니다.처리 되지 않은 예외의 위치 진단 후 작업 기반 연속 처리에 사용 합니다.

섹션 예외를 Throw 하는 런타임 이 문서에서 런타임 예외에 대해서는 작업 하는 방법에 설명 합니다.

Top

작업 그룹 및 병렬 알고리즘

이 단원에서는 작업 그룹에서 throw된 예외를 런타임에서 처리하는 방법에 대해 설명합니다.이 섹션에서는 또한 병렬 알고리즘에 같이 적용 concurrency::parallel_for, 작업 그룹에서 이러한 알고리즘을 작성 하기 때문에.

주의 정보주의

예외가 종속 작업에 미치는 효과를 이해해야 합니다.작업 또는 병렬 알고리즘에 예외 처리를 사용하는 방법에 대해 권장되는 방법은 병렬 패턴 라이브러리의 유용한 정보 항목에 있는 Understand how Cancellation and Exception Handling Affect Object Destruction 단원을 참조하십시오.

작업 그룹에 대한 자세한 내용은 작업 병렬 처리(동시성 런타임)를 참조하십시오.병렬 알고리즘에 대한 자세한 내용은 병렬 알고리즘을 참조하십시오.

전달 하는 작업 함수 본문에서 예외가 throw 한 concurrency::task_group 또는 concurrency::structured_task_group 개체에서 런타임에 예외를 저장 하 고 호출 컨텍스트를 마샬링하 concurrency::task_group::wait, concurrency::structured_task_group::wait, concurrency::task_group::run_and_wait, 또는 concurrency::structured_task_group::run_and_wait.또한 런타임에서는 자식 작업 그룹의 작업을 포함하여 작업 그룹에 있는 모든 활성 작업을 중지하고 아직 시작되지 않은 작업을 취소합니다.

다음 예제에서는 예외를 throw하는 작업 함수의 기본 구조를 보여 줍니다.이 예제에서는 task_group 개체를 사용하여 두 point 개체의 값을 병렬로 출력합니다.print_point 작업 함수는 point 개체의 값을 콘솔에 출력합니다.입력 값이 NULL인 경우 작업 함수에서 예외가 throw됩니다.런타임에서는 이 예외를 저장하고 task_group::wait를 호출하는 컨텍스트로 마샬링합니다.

// eh-task-group.cpp
// compile with: /EHsc
#include <ppl.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// Defines a basic point with X and Y coordinates.
struct point
{
   int X;
   int Y;
};

// Prints the provided point object to the console.
void print_point(point* pt)
{
   // Throw an exception if the value is NULL.
   if (pt == NULL)
   {
      throw exception("point is NULL.");
   }

   // Otherwise, print the values of the point.
   wstringstream ss;
   ss << L"X = " << pt->X << L", Y = " << pt->Y << endl;
   wcout << ss.str();
}

int wmain()
{
   // Create a few point objects.
   point pt = {15, 30};
   point* pt1 = &pt;
   point* pt2 = NULL;

   // Use a task group to print the values of the points.
   task_group tasks;

   tasks.run([&] {
      print_point(pt1);
   });

   tasks.run([&] {
      print_point(pt2);
   });

   // Wait for the tasks to finish. If any task throws an exception,
   // the runtime marshals it to the call to wait.
   try
   {
      tasks.wait();
   }
   catch (const exception& e)
   {
      wcerr << L"Caught exception: " << e.what() << endl;
   }
}

이 예제의 결과는 다음과 같습니다.

X = 15, Y = 30
Caught exception: point is NULL.

작업 그룹에서 예외 처리를 사용하는 전체 예제를 보려면 방법: 예외 처리를 사용하여 병렬 루프 중단을 참조하십시오.

Top

런타임에서 Throw되는 예외

호출 하는 런타임 예외가 발생할 수 있습니다.대부분의 예외 유형을 제외한 concurrency::task_canceledconcurrency::operation_timed_out, 프로그래밍 오류를 나타냅니다.일반적으로 이러한 오류는 복구할 수 없으므로 응용 프로그램 코드에 의해 처리되거나 catch되지 않아야 합니다.프로그래밍 오류를 진단해야 하는 경우에만 응용 프로그램 코드에서 복구할 수 없는 오류를 처리하거나 catch하는 것이 좋습니다.그러나 런타임에 정의되어 있는 예외 형식을 이해하면 프로그래밍 오류를 진단하는 데 도움이 될 수 있습니다.

런타임에서 throw되는 예외에 대한 예외 처리 메커니즘은 작업 함수에서 throw되는 예외의 경우와 같습니다.예를 들어 있는 concurrency::receive throw 작동 operation_timed_out 때이 하지 않는 메시지가 지정 된 기간 내에.작업 그룹에 전달하는 작업 함수에서 receive가 예외를 throw하는 경우 런타임에서는 해당 예외를 저장하고 task_group::wait, structured_task_group::wait, task_group::run_and_wait 또는 structured_task_group::run_and_wait를 호출하는 컨텍스트로 마샬링합니다.

다음 예제는 concurrency::parallel_invoke 알고리즘 두 가지 작업을 병렬로 실행 합니다.첫 번째 작업에서는 5초 동안 기다린 후 메시지 버퍼에 메시지를 보냅니다.두 번째 작업에서는 receive 함수를 사용하여 첫 번째 작업의 메시지 버퍼에서 메시지를 받을 때까지 3초 동안 기다립니다.receive 함수는 지정된 기간 내에 메시지를 받지 못하면 operation_timed_out을 throw합니다.

// eh-time-out.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   single_assignment<int> buffer;
   int result;

   try
   {
      // Run two tasks in parallel.
      parallel_invoke(
         // This task waits 5 seconds and then sends a message to 
         // the message buffer.
         [&] {
            wait(5000); 
            send(buffer, 42);
         },
         // This task waits 3 seconds to receive a message.
         // The receive function throws operation_timed_out if it does 
         // not receive a message in the specified time period.
         [&] {
            result = receive(buffer, 3000);
         }
      );

      // Print the result.
      wcout << L"The result is " << result << endl;
   }
   catch (operation_timed_out&)
   {
      wcout << L"The operation timed out." << endl;
   }
}

이 예제의 결과는 다음과 같습니다.

The operation timed out.

응용 프로그램이 비정상적으로 종료되지 않도록 하려면 코드에서 런타임을 호출할 때 예외를 처리해야 합니다.또한 타사 라이브러리와 같이 동시성 런타임을 사용하는 외부 코드를 호출할 때도 예외를 처리하십시오.

Top

여러 예외

작업 또는 병렬 알고리즘이 여러 예외를 받으면 런타임에서는 이러한 예외 중 하나만 호출 컨텍스트로 마샬링합니다.런타임에서는 어떤 예외를 마샬링하는지에 대해 보증하지 않습니다.

다음 예제에서는 parallel_for 알고리즘을 사용하여 숫자를 콘솔에 출력합니다.여기에서는 입력 값이 일부 최소값보다 작거나 최대값보다 크면 예외가 throw됩니다.이 예제에서는 여러 작업 함수에서 예외를 throw할 수 있습니다.

// eh-multiple.cpp
// compile with: /EHsc
#include <ppl.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

int wmain()
{
   const int min = 0;
   const int max = 10;

   // Print values in a parallel_for loop. Use a try-catch block to 
   // handle any exceptions that occur in the loop.
   try
   {
      parallel_for(-5, 20, [min,max](int i)
      {
         // Throw an exeception if the input value is less than the 
         // minimum or greater than the maximum.

         // Otherwise, print the value to the console.

         if (i < min)
         {
            stringstream ss;
            ss << i << ": the value is less than the minimum.";
            throw exception(ss.str().c_str());
         }
         else if (i > max)
         {
            stringstream ss;
            ss << i << ": the value is greater than than the maximum.";
            throw exception(ss.str().c_str());
         }
         else
         {
            wstringstream ss;
            ss << i << endl;
            wcout << ss.str();
         }
      });
   }
   catch (exception& e)
   {
      // Print the error to the console.
      wcerr << L"Caught exception: " << e.what() << endl;
   }  
}

이 예제를 실행하면 다음과 같은 샘플 결과가 출력됩니다.

8
2
9
3
10
4
5
6
7
Caught exception: -5: the value is less than the minimum.

Top

취소

모든 예외가 오류를 나타내는 것은 아닙니다.예를 들어 검색 알고리즘은 예외 처리를 사용하여 결과를 발견하면 관련 작업을 중지할 수 있습니다.코드에서 취소 메커니즘을 사용하는 방법에 대한 자세한 내용은 PPL에서의 취소를 참조하십시오.

Top

간단한 작업

간단한 작업이에서 직접 예약 하는 작업을 concurrency::Scheduler 개체.간단한 작업은 일반적인 작업보다 오버헤드가 적습니다.그러나 런타임에서는 간단한 작업에서 throw되는 예외를 catch하지 않습니다.대신 처리되지 않은 예외 처리기에 의해 예외가 catch되어 기본적으로 프로세스가 종료됩니다.그러므로 응용 프로그램에서 적절한 오류 처리 메커니즘을 사용해야 합니다.간단한 작업에 대한 자세한 내용은 작업 스케줄러(동시성 런타임)를 참조하십시오.

Top

비동기 에이전트

간단한 작업과 마찬가지로 런타임에서는 비동기 에이전트에서 throw되는 예외를 관리하지 않습니다.

다음 예제에서는 클래스에서 파생 되는 예외를 처리 하는 방법을 보여 줍니다. concurrency::agent.이 예제에서는 points_agent 클래스를 정의합니다.points_agent::run 메서드는 메시지 버퍼에서 point 개체를 읽고 콘솔에 출력합니다.run 메서드는 NULL 포인터를 받으면 예외를 throw합니다.

run 메서드는 try-catch 블록의 모든 작업을 포함합니다.catch 블록은 예외를 메시지 버퍼에 저장합니다.응용 프로그램에서는 에이전트가 끝난 후 이 버퍼를 읽어 해당 에이전트에서 오류가 발생했는지 여부를 검사합니다.

// eh-agents.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

// Defines a point with x and y coordinates.
struct point
{
   int X;
   int Y;
};

// Informs the agent to end processing.
point sentinel = {0,0};

// An agent that prints point objects to the console.
class point_agent : public agent
{
public:
   explicit point_agent(unbounded_buffer<point*>& points)
      : _points(points)
   { 
   }

   // Retrieves any exception that occurred in the agent.
   bool get_error(exception& e)
   {
      return try_receive(_error, e);
   }

protected:
   // Performs the work of the agent.
   void run()
   {
      // Perform processing in a try block.
      try
      {
         // Read from the buffer until we reach the sentinel value.
         while (true)
         {
            // Read a value from the message buffer.
            point* r = receive(_points);

            // In this example, it is an error to receive a 
            // NULL point pointer. In this case, throw an exception.
            if (r == NULL)
            {
               throw exception("point must not be NULL");
            }
            // Break from the loop if we receive the 
            // sentinel value.
            else if (r == &sentinel)
            {
               break;
            }
            // Otherwise, do something with the point.
            else
            {
               // Print the point to the console.
               wcout << L"X: " << r->X << L" Y: " << r->Y << endl;
            }
         }
      }
      // Store the error in the message buffer.
      catch (exception& e)
      {
         send(_error, e);
      }

      // Set the agent status to done.
      done();
   }

private:
   // A message buffer that receives point objects.
   unbounded_buffer<point*>& _points;

   // A message buffer that stores error information.
   single_assignment<exception> _error;
};

int wmain()
{  
   // Create a message buffer so that we can communicate with
   // the agent.
   unbounded_buffer<point*> buffer;

   // Create and start a point_agent object.
   point_agent a(buffer);
   a.start();

   // Send several points to the agent.
   point r1 = {10, 20};
   point r2 = {20, 30};
   point r3 = {30, 40};

   send(buffer, &r1);
   send(buffer, &r2);
   // To illustrate exception handling, send the NULL pointer to the agent.
   send(buffer, reinterpret_cast<point*>(NULL));
   send(buffer, &r3);
   send(buffer, &sentinel);

   // Wait for the agent to finish.
   agent::wait(&a);

   // Check whether the agent encountered an error.
   exception e;
   if (a.get_error(e))
   {
      cout << "error occurred in agent: " << e.what() << endl;
   }

   // Print out agent status.
   wcout << L"the status of the agent is: ";
   switch (a.status())
   {
   case agent_created:
      wcout << L"created";
      break;
   case agent_runnable:
      wcout << L"runnable";
      break;
   case agent_started:
      wcout << L"started";
      break;
   case agent_done:
      wcout << L"done";
      break;
   case agent_canceled:
      wcout << L"canceled";
      break;
   default:
      wcout << L"unknown";
      break;
   }
   wcout << endl;
}

이 예제의 결과는 다음과 같습니다.

X: 10 Y: 20
X: 20 Y: 30
error occurred in agent: point must not be NULL
the status of the agent is: done

try-catch 블록은 while 루프 외부에 있으므로 에이전트는 첫 번째 오류가 발생하면 처리를 종료합니다.try-catch 블록이 while 루프 내부에 있었다면 오류가 발생한 후에도 에이전트가 계속 실행됩니다.

이 예제에서는 다른 구성 요소에서 에이전트가 실행될 때 오류가 발생하는지 모니터링할 수 있도록 메시지 버퍼에 예외를 저장합니다.이 예제를 사용 하는 concurrency::single_assignment 오류를 저장 하는 개체입니다.에이전트가 여러 예외를 처리하는 경우 single_assignment 클래스는 첫 번째로 전달되는 메시지만 저장합니다.마지막 예외에만 저장할 수 있는 concurrency::overwrite_buffer 클래스.모든 예외를 저장할 수 있는 concurrency::unbounded_buffer 클래스입니다.이러한 메시지 블록에 대한 자세한 내용은 비동기 메시지 블록을 참조하십시오.

비동기 에이전트에 대한 자세한 내용은 비동기 에이전트를 참조하십시오.

Top

요약

Top

참고 항목

개념

동시성 런타임

작업 병렬 처리(동시성 런타임)

병렬 알고리즘

PPL에서의 취소

작업 스케줄러(동시성 런타임)

비동기 에이전트