O Runtime de simultaneidade de manipulação de exceção

O Runtime de simultaneidade usa para comunicar-se a vários tipos de erros de manipulação de exceção de C++. Esses erros incluem o uso inválido do runtime, erros de tempo de execução como, por exemplo, falha ao adquirir um recurso e erros que ocorrem em funções de trabalho que você fornecer aos grupos de tarefas. Este tópico descreve como o runtime manipula exceções lançadas por grupos de tarefas, tarefas leves e agentes assíncronos e como responder a exceções em seus aplicativos.

Seções

  • Grupos de tarefas e algoritmos paralelos

  • Tarefas leves

  • Agentes assíncronos

  • Exceções comuns

  • Resumo

Grupos de tarefas e algoritmos paralelos

Esta seção descreve como o runtime manipula exceções lançadas por grupos de tarefas. Esta seção também se aplica aos algoritmos paralelos, como Concurrency::parallel_for, porque esses algoritmos criar grupos de tarefas.

Aviso

Certifique-se de que entendeu os efeitos de exceções nas tarefas dependentes. Para obter as práticas recomendadas sobre como usar com tarefas ou os algoritmos paralelos de manipulação de exceção, consulte o Understand how Cancellation and Exception Handling Affect Object Destruction seção práticas recomendadas no tópico biblioteca paralela de padrões.

Para obter mais informações sobre grupos de tarefas, consulte Paralelismo de tarefas (Runtime de simultaneidade). Para obter mais informações sobre algoritmos paralelos, consulte Algoritmos paralelos.

Exceções geradas pelas funções de trabalho

A a função de trabalho é uma função lambda, um objeto de função ou um ponteiro de função que é passado para o tempo de execução. Quando você passar uma função de trabalho para um grupo de tarefas, o tempo de execução executa essa função de trabalho em um contexto separado.

Quando você lançar uma exceção no corpo de uma função de trabalho que você passa para um Concurrency::task_group ou Concurrency::structured_task_group o objeto, o runtime armazena essa exceção e dispõe o 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 runtime também interrompe todas as tarefas ativas que estão no grupo de tarefas (inclusive os grupos de tarefas filho) e descarta quaisquer tarefas que ainda não iniciaram.

O exemplo a seguir mostra a estrutura básica de uma função de trabalho que lança uma exceção. O exemplo usa um task_group o objeto para imprimir os valores de dois point objetos em paralelo. O print_point a função de trabalho imprime os valores de um point o objeto para o console. A função do trabalho lança uma exceção se o valor de entrada for NULL. O runtime armazena essa exceção e dispõe o 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.

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

Para obter um exemplo completo que usa um grupo de tarefas de manipulação de exceção, consulte Como: Use o tratamento de exceção para quebra de um Loop paralelo.

Exceções geradas pelo Runtime

Além de funções de trabalho, uma exceção pode resultar de uma chamada para o tempo de execução. A maioria das exceções que são lançadas pelo runtime indicam um erro de programação. Esses erros são normalmente irrecuperáveis e portanto devem não ser capturados ou manipulados pelo código do aplicativo. No entanto, Noções básicas sobre os tipos de exceção são definidos pelo runtime pode ajudar a diagnosticar erros de programação. A seção Exceções comuns descreve as exceções comuns e as condições sob as quais eles ocorrem.

A mecanismo de tratamento de exceção é o mesmo para exceções lançadas pelo tempo de execução como exceções lançadas por funções de trabalho. Por exemplo, o Concurrency::receive função throws operation_timed_out quando recebe uma mensagem no período de tempo especificado. Se receive lança uma exceção em uma função de trabalho que você passar para um grupo de tarefas, o runtime armazena essa exceção e dispõe o 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 a Concurrency::parallel_invoke o algoritmo executar duas tarefas em paralelo. A primeira tarefa espera cinco segundos e, em seguida, envia uma mensagem para um buffer de mensagem. A segunda tarefa usa o receive função aguardar três segundos para receber uma mensagem do mesmo buffer de mensagem. O receive lança a função operation_timed_out se não receber 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.

The operation timed out.

Para evitar o encerramento anormal do seu aplicativo, certifique-se de que o seu código manipula exceções quando ele chama no tempo de execução. Também manipular exceções quando você chama um código externo que usa o Runtime de simultaneidade, por exemplo, uma biblioteca de terceiros.

Várias exceções

Se um algoritmo de tarefa ou paralelo recebe várias exceções, o runtime empacota apenas uma dessas exceções para o contexto de chamada. O tempo de execução não garante que ele empacota de exceção.

O exemplo a seguir usa a parallel_for o algoritmo para imprimir os números no console. Ele lança uma exceção se o valor de entrada é menor que algum valor mínimo ou maior que algum valor máximo. Neste exemplo, várias funções de trabalho pode lançar 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 seguir mostra o exemplo de saída para esse exemplo.

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

Cancelamento

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

go to top

Tarefas leves

Uma tarefa simples é uma tarefa que você agendar diretamente de um Concurrency::Scheduler objeto. Tarefas leves transportar menos sobrecarga do que as tarefas comuns. No entanto, o runtime não capturar exceções lançadas por tarefas leves. Em vez disso, a exceção é capturada pelo manipulador de exceção não tratada, o que, por padrão, encerra o processo. Portanto, use um mecanismo de tratamento de erros apropriado em seu aplicativo. Para obter mais informações sobre tarefas leves, consulte Agendador de tarefas (Runtime de simultaneidade).

go to top

Agentes assíncronos

Como tarefas leves, o runtime não gerenciar exceções lançadas por agentes assíncronos.

O exemplo a seguir mostra uma maneira de tratar exceções em uma classe que deriva de Concurrency::agent. Este exemplo define o points_agent classe. O points_agent::run método lê point buffer de objetos a partir da mensagem e imprime-los ao console. O run método lança uma exceção se ele recebe um NULL ponteiro.

O run método envolve todo o trabalho em um try–catch bloco. O catch bloco armazena a exceção em um buffer de mensagem. O aplicativo verifica se o agente encontrou um erro lendo desse buffer após a conclusão do agente.

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

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

Porque o try–catch bloco existe fora o while loop, o agente termina de processamento quando encontrar o primeiro erro. Se a try–catch bloco foi dentro do while loop, o agente continuará após ocorrer um erro.

Este exemplo armazena exceções em um buffer de mensagem para que outro componente possa monitorar o agente de erros como ele é executado. Este exemplo usa um Concurrency::single_assignment o objeto para armazenar o erro. No caso em que um agente manipula exceções múltiplas, a single_assignment classe armazena apenas a primeira mensagem que é passada para o proprietário. Para armazenar somente a última exceção, use o Concurrency::overwrite_buffer classe. Para armazenar todas as exceções, use o Concurrency::unbounded_buffer classe. Para obter mais informações sobre esses blocos de mensagens, consulte Blocos de mensagens assíncronas.

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

go to top

Exceções comuns

A tabela a seguir lista as classes de exceção comum no Runtime de simultaneidade e fornece a condição sob a qual a exceção é lançada. A maioria dos tipos de exceção, exceto para operation_timed_out e unsupported_os, indicar um erro de programação. Esses erros são normalmente irrecuperáveis e portanto devem não ser capturados ou manipulados pelo código do aplicativo. Sugerimos que apenas capturar ou manipular erros irrecuperáveis no código do aplicativo quando você precisa diagnosticar erros de programação.

Classe de exceção

Condição

bad_target

Você pode passar um ponteiro inválido para um bloco de mensagens.

context_self_unblock

Um contexto tentou desbloquear propriamente dito.

context_unblock_unbalanced

O runtime tentou desbloquear um contexto, mas esse contexto já está desbloqueado.

default_scheduler_exists

Um Agendador foi tentado a ser definida como o agendador padrão, mas um agendador padrão já existe.

improper_lock

Um bloqueio foi adquirido incorretamente.

improper_scheduler_attach

Um contexto foi anexado ao Agendador do mesmo várias vezes.

improper_scheduler_detach

Um contexto que é gerenciado internamente pelo runtime foi desanexado do seu Agendador ou o contexto não está associado a qualquer programador.

improper_scheduler_reference

Um contexto incrementado o contador de referência em um agendador que está sendo desligado e que o contexto que não é o Agendador interno.

invalid_link_target

O mesmo objeto está vinculado a um bloco de mensagem, mais uma vez.

invalid_multiple_scheduling

Mais uma vez foi agendada uma tarefa que não foi concluída.

invalid_operation

O runtime executou uma operação inválida.

invalid_oversubscribe_operation

Excesso de assinatura foi desativado quando ele não foi ativado.

invalid_scheduler_policy_key

Uma chave de política inválido foi fornecida para um Concurrency::SchedulerPolicy objeto.

invalid_scheduler_policy_thread_specification

A SchedulerPolicy objeto é especificado para ter um nível de concorrência máxima que é menor do que o nível de simultaneidade mínima.

invalid_scheduler_policy_value

Um valor de política inválido foi fornecido para um SchedulerPolicy objeto.

message_not_found

Um bloco de mensagens não pôde encontrar uma mensagem solicitada.

missing_wait

Um objeto de grupo de tarefas foi destruído antes de Concurrency::task_group::wait método ou a Concurrency::structured_task_group::wait o método foi chamado.

nested_scheduler_missing_detach

Um Agendador aninhado não corretamente desanexar próprio do pai.

operation_timed_out

Uma operação não foi concluída no período de tempo especificado.

scheduler_not_attached

Um contexto tentou desconectar-se de que seu Agendador, mas o contexto não foi anexado a qualquer programador.

scheduler_resource_allocation_error

O runtime não obteve um recurso crítico, por exemplo, um recurso é fornecido pelo sistema operacional.

unsupported_os

O tempo de execução não é suportado no sistema operacional atual.

go to top

Resumo

Quando uma tarefa lança uma exceção, o runtime mantém essa exceção e dispõe o contexto que aguarda o grupo de tarefas concluir. Para componentes como, por exemplo, agentes e tarefas leves, o runtime não gerenciar exceções para você. Nesses casos, você deve implementar seu próprio mecanismo de tratamento de exceção.

go to top

Consulte também

Conceitos

Runtime de simultaneidade

Paralelismo de tarefas (Runtime de simultaneidade)

Algoritmos paralelos

Cancelamento na PPL

Agendador de tarefas (Runtime de simultaneidade)

Agentes assíncronos

Histórico de alterações

Date

History

Motivo

Março de 2011

Adicionada uma nota de advertência sobre os efeitos que podem ter exceções tarefas dependentes.

Aprimoramento de informações.