Compartilhar via


Tratamento de exceções no tempo de execução de simultaneidade

O tempo de execução de simultaneidade usa a manipulação de exceção do C++ para comunicar muitos tipos de erros. Esses erros incluem o uso inválido de tempo de execução, de erros de tempo de execução como a falha adquirir um recurso, e de erros que ocorrem em funções de trabalho que você fornece às tarefas e os grupos de trabalho. Quando uma tarefa ou um grupo de trabalho lança uma exceção, o tempo de execução que retém exceção e realiza marshaling ao contexto espera que a tarefa ou no grupo de trabalho concluída. Para componentes como tarefas leves e agentes, o tempo de execução não gerencia exceções para você. Nesses casos, você deve implementar seu próprio mecanismo manipulação de exceções gerais. Este tópico descreve como o tempo de execução trata as exceções emitidas por tarefas, por grupos de trabalho, por tarefas de peso leve, e por agentes assíncronas, e como responder às exceções em seus aplicativos.

Pontos-chave

  • Quando uma tarefa ou um grupo de trabalho lança uma exceção, o tempo de execução que retém exceção e realiza marshaling ao contexto espera que a tarefa ou no grupo de trabalho concluída.

  • Quando possível, coloque cada chamada para concurrency::task::get e a concurrency::task::wait com um bloco de try/catch para tratar erros de que você pode recuperar. O tempo de execução termina o aplicativo se uma tarefa gerencie uma exceção e essa exceção não for capturada pela tarefa, uma de suas continuações, ou o aplicativo principal.

  • Execuções baseadas de uma acompanhamento sempre; não importa se a tarefa antecedente concluída com êxito, lance uma exceção, ou tiver sido cancelada. Um acompanhamento baseada em valor não será executado se a tarefa antecedente gerencie ou cancela.

  • Como as continuações baseadas sempre execução, consulte se adicionar uma tarefa continuar com base no final da cadeia de continuação. Isso pode ajudar a garantir que seu código observe todas as exceções.

  • O tempo de execução gerencie concurrency::task_canceled quando você chama concurrency::task::get e a tarefa for cancelada.

  • O tempo de execução não gerencia exceções para tarefas e agentes de peso leve.

Neste Documento

  • Tarefas e Continuações

  • Grupos de Tarefas e Algoritmos Paralelos

  • Exceções Geradas pelo Tempo de Execução

  • Várias Exceções

  • Cancelamento

  • Tarefas Leves

  • Agentes Assíncronos

Tarefas e Continuações

Esta seção descreve como o tempo de execução trata as exceções emitidas por objetos de concurrency::task e pelas continuações. Para obter mais informações sobre a tarefa e do modelo de acompanhamento, consulte Paralelismo de tarefa (tempo de execução de simultaneidade).

Quando você gerencie uma exceção no corpo de uma função de trabalho que você transmita a um objeto de task , o tempo de execução que armazena exceção e realiza marshaling ao contexto que chama concurrency::task::get ou concurrency::task::wait. O documento Paralelismo de tarefa (tempo de execução de simultaneidade) descreve tarefas com base em continuações valores com base, mas para resumir, uma acompanhamento baseada em valor leva um parâmetro do tipo T e uma acompanhamento de tarefas com base usar um parâmetro do tipo task<T>. Se uma tarefa que gerencie tem uma ou mais continuações valores com base, essas continuações não estão agendado para execução. O exemplo a seguir ilustra esse comportamento:

// 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.
*/

Um acompanhamento de tarefas com base permite tratar todas a exceção que é gerada pela tarefa antecedente. Execuções baseadas de uma acompanhamento sempre; não importa se a tarefa foi concluída com êxito, lance uma exceção, ou tiver sido cancelada. Quando uma tarefa gerará uma exceção, suas continuações tarefas agendadas para serem executadas são baseadas. O exemplo a seguir mostra uma tarefa que gerencie sempre. A tarefa tem duas continuações; um - se baseia e outras tarefas é baseado. As execuções baseadas de exceção sempre consequentemente, e podem capturar a exceção gerada pela tarefa antecedente. Quando o exemplo espera ambas as continuações para ser concluída, a exceção será gerada novamente porque a exceção da tarefa sempre é lançada quando task::get ou task::wait são chamados.

// 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.
*/

Recomendamos que você use continuações baseadas para capturar exceções que você pode lidar. Como as continuações baseadas sempre execução, consulte se adicionar uma tarefa continuar com base no final da cadeia de continuação. Isso pode ajudar a garantir que seu código observe todas as exceções. O exemplo a seguir mostra uma cadeia baseada em valor básica de continuação. A terceira tarefa em gera de cadeia e, consequentemente nenhuma continuações baseada em valor que se seguem não é executada. No entanto, a continuação final tarefa é baseada e, consequentemente sempre é executado. Essa acompanhamento final trata a exceção que é lançada pela terceira tarefa.

É recomendável capturar exceções as mais específicas que você pode. Você pode omitir essa acompanhamento de tarefas com base final se você não tiver as exceções específicas a capturar. Qualquer exceção sem-tratamento permanecerá e pode finalizar o aplicativo.

// 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>
*/

Dica

Você pode usar o método de concurrency::task_completion_event::set_exception para associar uma exceção com um evento de conclusão da tarefa.O documento Paralelismo de tarefa (tempo de execução de simultaneidade) descreve a classe de concurrency::task_completion_event em mais detalhes.

concurrency::task_canceled é um tipo de exceção importante de tempo de execução que se relaciona a task. O tempo de execução gerencie task_canceled quando você chama task::get e a tarefa for cancelada. (Por outro lado, task::wait retorna task_status::canceled e não o lança.) Você pode capturar e manipular essa exceção de uma acompanhamento de tarefas com base ou quando você chamar task::get. Para obter mais informações sobre o cancelamento da tarefa, consulte Cancelamento no PPL.

Aviso

Nunca gerencie task_canceled de seu código.Chame concurrency::cancel_current_task em vez disso.

O tempo de execução termina o aplicativo se uma tarefa gerencie uma exceção e essa exceção não for capturada pela tarefa, uma de suas continuações, ou o aplicativo principal. Se seu aplicativo falhar, você pode configurar o Visual Studio para interromper quando as exceções C++ são geradas. Depois que você diagnostica o local da exceção sem-tratamento, use uma acompanhamento de tarefas com base para tratá-la.

A seção Exceções lançadas em tempo de execução neste documento descreve como trabalhar em maiores detalhes com exceções em tempo de execução.

[Superior]

Grupos de Tarefas e Algoritmos Paralelos

Esta seção descreve como o tempo de execução trata as exceções emitidas por grupos de trabalho. Esta seção também se aplica aos algoritmos paralelos como concurrency::parallel_for, pois esses algoritmos criam em grupos de trabalho.

Aviso

Verifique se entendeu os efeitos que têm as exceções em tarefas dependentes.Para obter as práticas recomendadas sobre como usar a manipulação de exceção com tarefas ou algoritmos paralelos, consulte a seção de Entenda como o efeito de manipulação de cancelamento e destruição objetos de exceção nas práticas recomendadas no tópico de biblioteca de padrões de paralela.

Para obter mais informações sobre grupos de trabalho, consulte Paralelismo de tarefa (tempo de execução de simultaneidade). Para obter mais informações sobre algoritmos paralelos, consulte Algoritmos paralelos.

Quando você gerencie uma exceção no corpo de uma função de trabalho que você transmita a um objeto de concurrency::task_group ou de concurrency::structured_task_group , o tempo de execução que armazena exceção e realiza marshaling ao contexto que chama concurrency::task_group::wait, concurrency::structured_task_group::wait, concurrency::task_group::run_and_wait, ou concurrency::structured_task_group::run_and_wait. O tempo de execução também para todas as tarefas ativas que estão no grupo de trabalho (inclusive aqueles em grupos de trabalho filhos) e em descarta todas as tarefas que ainda não sejam iniciados.

O exemplo a seguir mostra a estrutura básica de uma função de trabalho que gerencie uma exceção. O exemplo usa um objeto de task_group para imprimir em paralelo os valores de dois objetos de point . A função de trabalho de print_point imprime os valores de um objeto de point ao console. A função de trabalho lançará uma exceção se o valor de entrada é NULL. O tempo de execução armazena essa exceção e realiza marshaling ao contexto que chama 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;
   }
}

O exemplo produz a seguinte saída.

  

Para obter um exemplo completo que usa a manipulação de exceção em um grupo de trabalho, consulte Como usar tratamento de exceções para parar um loop paralelo.

[Superior]

Exceções Geradas pelo Tempo de Execução

Uma exceção pode resultar de uma chamada ao tempo de execução. A maioria dos tipos de exceção, com exceção de concurrency::task_canceled e de concurrency::operation_timed_out, indica um erro de programação. Esses erros são geralmente irrecuperáveis consequentemente, e não devem ser capturados ou tratado pelo código de aplicativo. Nós sugerimos que você só capture ou trata erros irrecuperável em seu código de aplicativo quando precisar diagnosticar erros de programação. No entanto, entender os tipos de exceção que são definidos no tempo de execução pode ajudar a diagnosticar erros de programação.

O mecanismo de manipulação de exceção é o mesmo para as exceções emitidas em tempo de execução como as exceções emitidas por funções de trabalho. Por exemplo, a função de concurrency::receive gerencie operation_timed_out quando não receber uma mensagem no período de tempo especificado. Se receive gerará uma exceção em uma função de trabalho que você transmita a um grupo de trabalho, o tempo de execução que armazena exceção e realiza marshaling ao contexto que chama task_group::wait, structured_task_group::wait, task_group::run_and_wait, ou structured_task_group::run_and_wait.

O exemplo a seguir usa o algoritmo de concurrency::parallel_invoke para ser executados em paralelo duas tarefas. A primeira tarefa espera cinco segundos e então envia uma mensagem para um buffer de mensagem. A segunda tarefa usa a função de receive três segundos a esperar para receber uma mensagem do mesmo buffer de mensagem. A função de receive gerencie operation_timed_out se não recebe a mensagem no período de tempo.

// 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;
   }
}

O exemplo produz a seguinte saída.

  

Para evitar o encerramento anormal do aplicativo, verifique se seu código trata exceções quando chama em tempo de execução. Também tratar exceções quando você chama no código externo que usa o tempo de execução de simultaneidade, por exemplo, uma biblioteca de terceiros.

[Superior]

Várias Exceções

Se uma tarefa ou um algoritmo paralelo recebem mais exceções, os realiza marshaling apenas um de tempo de execução dessas exceções ao contexto de chamada. O tempo de execução não garante que ele realiza marshaling exceção.

O exemplo a seguir usa o algoritmo de parallel_for para imprimir números no console. Lançará uma exceção se o valor de entrada é menor do que qualquer valor mínimo ou maior que qualquer valor máximo. Neste exemplo, as várias funções de trabalho podem gerar uma exceção.

// 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;
   }  
}

A saída de exemplo a seguir mostra exemplos de para este exemplo.

  

[Superior]

Cancelamento

Nem todas as exceções indica um erro. Por exemplo, um algoritmo de pesquisa pode usar a manipulação de exceção para interromper a tarefa associada quando encontrar o resultado. Para obter mais informações sobre como usar os mecanismos de cancelamento em seu código, consulte Cancelamento no PPL.

[Superior]

Tarefas Leves

Uma tarefa de peso leve é uma tarefa que você agende diretamente de um objeto de concurrency::Scheduler . As tarefas de peso leve menos sobrecarga do que levam tarefas comuns. No entanto, o tempo de execução não captura as exceções emitidas por tarefas de peso leve. Em vez disso, a exceção será capturada pelo manipulador de exceção sem-tratamento, que finaliza por padrão o processo. Em virtude disso, use um mecanismo de tratamento de erro apropriado em seu aplicativo. Para obter mais informações sobre as tarefas de peso leve, consulte Agendador de tarefas (Tempo de Execução de Simultaneidade).

[Superior]

Agentes Assíncronos

Como tarefas superficial, o tempo de execução não gerencia as exceções emitidas por agentes assíncronas.

O exemplo a seguir mostra uma maneira de tratar exceções em uma classe que deriva de concurrency::agent. Este exemplo define a classe de points_agent . O método de points_agent::run ler objetos de point do buffer de mensagem e imprime-os ao console. O método de run lançará uma exceção se receber um ponteiro de NULL .

O método de run o redor de qualquer trabalho em um bloco de try- decatch . O bloco de catch armazena a exceção em um buffer de mensagem. O aplicativo verifica se o agent encontrar um erro faz a leitura a partir desse buffer depois que o agente é concluída.

// 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;
}

O exemplo produz a seguinte saída.

  

Como try- o bloco decatch existem fora do loop de while , o agente termina o processamento quando encontrar o primeiro erro. Se try- o bloco decatch estava em loop for, o agente de while continuaria depois que um erro ocorrer.

Este exemplo armazena exceções em um buffer de mensagem de modo que outro componente possa monitorar o agent para erros à medida que é executado. Este exemplo usa um objeto de concurrency::single_assignment para armazenar o erro. Nos casos em que um agente trata mais exceções, a classe de single_assignment armazena apenas a primeira mensagem que ele seja excedido. Para armazenar apenas à última exceção, use a classe de concurrency::overwrite_buffer . Para armazenar todas as exceções, use a classe de concurrency::unbounded_buffer . Para obter mais informações sobre esses blocos de mensagem, consulte Blocos de mensagens assíncronos.

Para obter mais informações sobre os agentes assíncronas, consulte Agentes assíncronos.

[Superior]

Resumo

[Superior]

Consulte também

Conceitos

Tempo de Execução de Simultaneidade

Paralelismo de tarefa (tempo de execução de simultaneidade)

Algoritmos paralelos

Cancelamento no PPL

Agendador de tarefas (Tempo de Execução de Simultaneidade)

Agentes assíncronos