Partilhar via


Manipulação de exceção em tempo de execução de concorrência

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

Chave

  • Quando uma tarefa ou um grupo de trabalho lança uma exceção, o tempo de execução comporta que exceção e ele lê ao contexto que espera a tarefa ou o grupo de trabalho concluído.

  • Quando possível, coloque cada chamada a concurrency::task::get e a concurrency::task::wait com um bloco de try/catch para manipular erros de que você pode recuperar.O runtime finaliza o aplicativo se uma tarefa lança uma exceção e essa exceção não é detectada por tarefa, uma de suas continuações, ou o aplicativo principal.

  • Executa chave com base de uma continuação sempre; não importa se a tarefa antecedente concluída com êxito, acione uma exceção, nem foi cancelado.Uma continuação valor base não é executado se a tarefa antecedente gera ou cancela.

  • Porque as continuações chave com base sempre execução, consulte se uma continuação chave com base no final da cadeia de continuação de linha.Isso pode ajudar a garantir que seu código observa todas as exceções.

  • O tempo de execução gera concurrency::task_canceled quando você chama concurrency::task::get e essa tarefa é cancelada.

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

Neste documento

  • Tarefas e continuações

  • Grupos de trabalho e algoritmos paralelos

  • Exceções geradas pelo tempo de execução

  • Várias exceções

  • Cancelamento

  • Tarefas leve

  • Agentes assíncronos

Tarefas e continuações

Esta seção descreve como o tempo de execução trata exceções que são geradas por objetos de concurrency::task e por suas continuações.Para obter mais informações sobre a tarefa e o modelo de continuação de linha, consulte Paralelismo de tarefa (tempo de execução de simultaneidade).

Quando você gera uma exceção no corpo de uma função de trabalho que você passe a um objeto de task , o tempo de execução que armazena exceção e ele lê ao contexto que chama concurrency::task::get ou concurrency::task::wait.O documento Paralelismo de tarefa (tempo de execução de simultaneidade) descreve chave com base em continuações valor com base, mas para resumir, uma continuação valor base tem um parâmetro de tipo T e continuação uma chave com base tem um parâmetro de tipo task<T>.Se uma tarefa que gere tem uma ou mais continuações valor com base, essas continuações não são agendadas para executar.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.
*/

Continuação uma chave com base permite que você manipule qualquer exceção que é lançada por tarefa antecedente.Executa chave com base de uma continuação sempre; não importa se a tarefa concluída com êxito, acione uma exceção, nem foi cancelado.Quando uma tarefa gera uma exceção, suas continuações chave com base são agendadas para executar.O exemplo a seguir mostra uma tarefa que gere sempre.A tarefa tem duas continuações; um valor é baseado e a outra tarefa é baseado.O é executado chave com base de exceção sempre, e podem como consequência capturar a exceção que é lançada por tarefa antecedente.Quando o exemplo espera ambas as continuações para concluir, a exceção é lançada novamente porque a tarefa de exceção é lançada sempre 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ê usa continuações chave com base para capturar exceções que você pode manipular.Porque as continuações chave com base sempre execução, consulte se uma continuação chave com base no final da cadeia de continuação de linha.Isso pode ajudar a garantir que seu código observa todas as exceções.O exemplo a seguir mostra uma cadeia valor base básica de continuação de linha.A terceira tarefa o gera de cadeia continuações como consequência, e nenhuma valor base que a seguir não é executada.No entanto, continuação a tarefa final é baseada, e como consequência sempre executa.Esta continuação final lida com a exceção que é lançada pela terceira tarefa.

Recomendamos que você capture as exceções mais específicas que possível.Você pode omitir esta tarefa final em continuação se você não precisa capturar exceções específicas para.Qualquer exceção não tratada e permanecerá 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>
*/
DicaDica

Você pode usar o método de concurrency::task_completion_event::set_exception para associar uma exceção com um evento de conclusão de tarefas.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 em tempo de execução que está relacionado a task.O tempo de execução gera task_canceled quando você chama task::get e essa tarefa é cancelada.(Por outro lado, task::wait retorna task_status::canceled e não o gera uma.) Você pode capturar e tratar essa exceção de uma continuação chave com base ou quando você chamar task::get.Para obter mais informações sobre cancelar de tarefas, consulte Cancelar o PPL.

Observação de cuidadoCuidado

Nunca lançar task_canceled do seu código.Chamada concurrency::cancel_current_task em vez disso.

O runtime finaliza o aplicativo se uma tarefa lança uma exceção e essa exceção não é detectada por tarefa, uma de suas continuações, ou o aplicativo principal.Se seu aplicativo falha, você pode configurar o Visual Studio para interromper quando exceções C++ são geradas.Depois que você diagnostica o local de exceção não tratada, use uma continuação chave com base para tratá-lo.

A seção Exceções geradas pelo tempo de execução neste documento descreve detalhadamente como trabalhar com exceções de tempo de execução.

Superior[]

Grupos de trabalho e algoritmos paralelos

Esta seção descreve como o tempo de execução trata exceções que são geradas por grupos de trabalho.Esta seção também se aplica aos algoritmos paralelos como concurrency::parallel_for, porque esses algoritmos criados em grupos de trabalho.

Observação de cuidadoCuidado

Certifique-se de que você entende efeitos que as exceções têm tarefas dependentes.Para práticas recomendadas sobre como usar manipulação de exceção com tarefas ou algoritmos paralelos, consulte a seção de Entenda como o efeito de manipulação de exceção e cancelar a destruição objetos nas práticas recomendadas no tópico de biblioteca dos 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 os algoritmos paralelos, consulte Algoritmos paralelos.

Quando você gera uma exceção no corpo de uma função de trabalho que você passe a um objeto de concurrency::task_group ou de concurrency::structured_task_group , o tempo de execução que armazena exceção e ele lê 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 runtime também para todas as tarefas ativas que estão no grupo de trabalho (incluindo aqueles em grupos de trabalho filho) e em descarta todas as tarefas que ainda não começarem.

O exemplo a seguir mostra a estrutura básica de uma função de trabalho que gere uma exceção.O exemplo usa um objeto de task_group para imprimir paralelamente os valores dos dois objetos de point .A função de trabalho de print_point imprime valores de um objeto de point no console.A função de trabalho gera uma exceção se o valor da entrada é NULL.O tempo de execução armazena essa exceção e ele lê 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 um exemplo completo que use manipulação de exceção em um grupo de trabalho, consulte Como: Use manipulação de exceção para interromper de um loop paralelo.

Superior[]

Exceções geradas pelo tempo de execução

Uma exceção pode resultar de uma chamada para o tempo de execução.A maioria dos tipos de exceção, a exceção de concurrency::task_canceled e de concurrency::operation_timed_out, indicam um erro de programação.Esses erros são normalmente irrecuperável como consequência, e não devem ser detectados ou manipulado pelo código do aplicativo.Sugerimos que você capture somente manipula erros irrecuperável ou em seu código do aplicativo quando você precisa diagnosticar erros de programação.No entanto, compreender 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 exceções que são geradas pelo tempo de execução como as exceções que são geradas por funções de trabalho.Por exemplo, a função de concurrency::receive gera operation_timed_out quando não recebe uma mensagem o período de tempo especificado.Se receive lança uma exceção em uma função de trabalho que você passe a um grupo de trabalho, o tempo de execução que armazena exceção e ele lê 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 executar paralelamente duas tarefas.A primeira tarefa espera cinco segundos e envia em uma mensagem de um buffer de mensagem.A segunda tarefa usa a função para esperar receive de três segundos para receber uma mensagem do mesmo buffer de mensagem.A função de receive gera operation_timed_out se não recebe a mensagem o 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 seu aplicativo, certifique-se que o código trata exceções quando chama em tempo de execução.Também manipular exceções quando você chama no código externo que usa o tempo de execução de concorrência, por exemplo, uma biblioteca de terceiros.

Superior[]

Várias exceções

Se uma tarefa ou um algoritmo paralelo recebem várias exceções, os lê apenas um de tempo de execução dessas exceções no contexto de chamada.O tempo de execução não garante que ele controla exceção.

O exemplo a seguir usa o algoritmo de parallel_for para imprimir números no console.Gera uma exceção se o valor da entrada for menor do que qualquer valor mínimo ou maior do que qualquer valor máximo.Nesse exemplo, várias funções de trabalho podem 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 seguinte saída de exemplo mostra para esse exemplo.

  

Superior[]

Cancelamento

Nem todas as exceções indicam um erro.Por exemplo, um algoritmo de pesquisa pode usar tratamento de exceções para interromper sua tarefa associada quando encontrar o resultado.Para obter mais informações sobre como utilizar mecanismos de cancelamento em seu código, consulte Cancelar o PPL.

Superior[]

Tarefas leve

Uma tarefa leve é uma tarefa que você agende diretamente de um objeto de concurrency::Scheduler .As tarefas leve levam menos sobrecarga de tarefas comuns.No entanto, o tempo de execução não captura exceções que são geradas por tarefas leve.Em vez disso, a exceção é detectada pelo manipulador de exceção sem tratamento que finaliza, por padrão o processo.Como consequência, use um mecanismo de tratamento de erros apropriado em seu aplicativo.Para obter mais informações sobre as tarefas leve, consulte Agendador de tarefa (tempo de execução de simultaneidade).

Superior[]

Agentes assíncronos

Como tarefas leve, o tempo de execução não gerencia as exceções que são geradas por agentes assíncronas.

O exemplo a seguir mostra uma maneira para 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 no console.O método de run gera uma exceção se recebe um ponteiro de NULL .

O método de run redor de todo o 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 agente localize um erro ler do buffer depois que o agente for 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.

  

Porque try- o bloco decatch existe fora do loop de while , o agente termina o processamento quando encontra o primeiro erro.Se try- o bloco decatch estava dentro do loop de while , o agente continuaria após ocorrerá um erro.

Este exemplo armazena exceções em um buffer de mensagem para que outro componente pode monitorar o agente para erros que é executado.Este exemplo usa um objeto de concurrency::single_assignment para armazenar o erro.Em casos onde um agente manipula múltiplos exceções, a classe de single_assignment armazena apenas a primeira mensagem que ela é passada.Para armazenar somente a exceção mais recente, 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 assíncronas de mensagem.

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 concorrência

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

Algoritmos paralelos

Cancelar o PPL

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

Agentes assíncronos