Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
El Runtime de simultaneidad usa el control de excepciones de C++ para comunicar muchas modalidades de errores.Estos errores se incluyen el uso no válido del runtime, errores de runtime como error adquirir un recurso, y errores que se producen en las funciones de trabajo que se proporciona a las tareas y los grupos de tareas.Cuando una tarea o un grupo de tareas produce una excepción, los bloqueos de runtime esa excepción y los calcular las referencias en el contexto que espera la tarea o grupo de tareas finalicen.En el caso de componentes tales como tareas ligeras y agentes, el runtime no administra las excepciones en nombre del usuario.En estos casos, debe implementar su propio mecanismo del control de excepciones.En este tema se describe cómo el runtime administra las excepciones producidas por las tareas, grupos de tareas, las tareas ligeras, y agentes asincrónicos, y cómo responder a las excepciones en las aplicaciones.
Puntos clave
Cuando una tarea o un grupo de tareas produce una excepción, los bloqueos de runtime esa excepción y los calcular las referencias en el contexto que espera la tarea o grupo de tareas finalicen.
Cuando es posible, incluya cada llamada a concurrency::task::get y a concurrency::task::wait con un bloque de try/decatch para controlar los errores de los que puede recuperar.El runtime finaliza la aplicación si una tarea produce una excepción y esa excepción no detectada por la tarea, una de las continuaciones, o la aplicación principal.
Ejecuta tarea- en función de una continuación siempre; independientemente de si la tarea antecedente se completó correctamente, lanzara una excepción, ni se canceló.Una continuación siguiente valor basada no se ejecuta si la tarea antecedente produce o cancela.
Dado que la ejecución tarea- basado de las continuaciones siempre, vea si agregar una continuación tarea- basada en el final de la cadena de continuación.Esto puede ayudar a garantizar que el código inspecciona todas las excepciones.
El runtime produce concurrency::task_canceled cuando se llama a concurrency::task::get y esa tarea se cancela.
El runtime no administra las excepciones para las tareas y los agentes ligeros.
En este documento
Tareas y Continuaciones
Grupos de tareas y algoritmos paralelos
Excepciones que produce el runtime
Varias excepciones
Cancelación
Tareas ligeras
Agentes asincrónicos
Tareas y Continuaciones
En esta sección se describe cómo el runtime controla las excepciones producidas por los objetos de concurrency::task y sus continuaciones.Para obtener más información sobre la tarea y el modelo de continuación, vea Paralelismo de tareas (Runtime de simultaneidad).
Cuando se produce una excepción en el cuerpo de una función de trabajo que se pasa a task un objeto, el runtime almacena esa excepción y los calcular las referencias en el contexto que llama a concurrency::task::get o concurrency::task::wait.El documento Paralelismo de tareas (Runtime de simultaneidad) describe tarea- basado en continuaciones siguiente valor basadas en, pero resumir, una continuación siguiente valor basada toma un parámetro de T escrito y una continuación tarea- basada toma un parámetro de **task<T>**escrito.Si una tarea que produce tiene una o varias continuaciones siguiente valor basadas en, esas continuaciones no se han programado para ejecutarse.En el siguiente ejemplo, se muestra este comportamiento:
// 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.
*/
Una continuación tarea- basada permite administrar cualquier excepción producida por la tarea antecedente.Ejecuta tarea- en función de una continuación siempre; independientemente de si la tarea completada correctamente, lanzara una excepción, ni se canceló.Cuando una tarea produce una excepción, sus continuaciones tarea- basadas se han programado para ejecutarse.El ejemplo siguiente se muestra una tarea que produce siempre.La tarea tiene dos continuaciones; siguiente valor se basa uno y tarea- está basado en el otro.Se ejecuta tarea- basados de excepción siempre, y por consiguiente pueden detectar la excepción que produce la tarea antecedente.Cuando el ejemplo espera ambas continuaciones para terminar, la excepción se produce de nuevo porque la excepción de la tarea se produce cuando se llama a task::get o 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.
*/
Se recomienda usar continuaciones tarea- basadas para detectar excepciones que puede administrar.Dado que la ejecución tarea- basado de las continuaciones siempre, vea si agregar una continuación tarea- basada en el final de la cadena de continuación.Esto puede ayudar a garantizar que el código inspecciona todas las excepciones.El ejemplo siguiente se muestra una cadena siguiente valor basada básica de continuación.La tercera tarea en los tiros de cadena, y por consiguiente ninguna continuaciones siguiente valor basada que la cumplen no se ejecuta.Sin embargo, tarea- se basa la continuación final y, por consiguiente siempre se ejecuta.Esta continuación final controla la excepción que produce la tercera tarea.
Se recomienda detecta las excepciones más específicas que puede.Puede omitir esta continuación tarea- basada final si no tiene excepciones específicas para detectar.Cualquier excepción seguirá no controlada y puede finalizar la aplicación.
// 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>
*/
Sugerencia |
|---|
Puede utilizar el método de concurrency::task_completion_event::set_exception para asociar una excepción con un evento de finalización de la tarea.El documento Paralelismo de tareas (Runtime de simultaneidad) describe el tipo de concurrency::task_completion_event con mayor detalle. |
concurrency::task_canceled es un tipo de excepción importante del runtime que se relaciona con task.El runtime produce task_canceled cuando se llama a task::get y esa tarea se cancela.(Al contrario, task::wait devuelve task_status::canceled y no se produce.) Puede detectar y controlar esta excepción de una continuación tarea- basada o cuando se llama a task::get.Para obtener más información sobre la cancelación de la tarea, vea Cancelación en la biblioteca PPL.
Precaución |
|---|
Nunca produce task_canceled del código.Llamada concurrency::cancel_current_task en su lugar. |
El runtime finaliza la aplicación si una tarea produce una excepción y esa excepción no detectada por la tarea, una de las continuaciones, o la aplicación principal.Si la aplicación se bloquea, puede configurar Visual Studio para interrumpir cuando se producen excepciones de C++.Tras diagnosticar la ubicación de la excepción no controlada, utilice una continuación tarea- basada para controlarla.
La sección Excepciones Que produce el runtime en este documento se describe cómo trabajar con excepciones del runtime con mayor detalle.
[parte superior]
Grupos de tareas y algoritmos paralelos
En esta sección se describe cómo controla el runtime las excepciones que producen los grupos de tareas.Esta sección también se aplica a los algoritmos paralelos como concurrency::parallel_for, porque estos algoritmos se basan en grupos de tareas.
Precaución |
|---|
Asegúrese de que entiende los efectos que tienen las excepciones sobre las tareas dependientes.Para conocer los procedimientos recomendados sobre cómo usar el control de excepciones con tareas o algoritmos paralelos, vea la sección Understand how Cancellation and Exception Handling Affect Object Destruction del tema Procedimientos recomendados de la Biblioteca de modelos de procesamiento paralelo. |
Para obtener más información acerca de los grupos de tareas, vea Paralelismo de tareas (Runtime de simultaneidad).Para obtener más información acerca de los algoritmos paralelos, vea Algoritmos paralelos.
Cuando se produce una excepción en el cuerpo de una función de trabajo que se pasa a un objeto de concurrency::task_group o de concurrency::structured_task_group , el runtime almacena esa excepción y los calcular las referencias en el contexto que llama a concurrency::task_group::wait, concurrency::structured_task_group::wait, concurrency::task_group::run_and_wait, o concurrency::structured_task_group::run_and_wait.El runtime también detiene todas las tareas activas que están en el grupo de tareas (incluidas las que se encuentran en grupos de tareas secundarios) y descarta cualquier tarea que no se haya iniciado todavía.
En el siguiente ejemplo se muestra la estructura básica de una función de trabajo que produce una excepción.En el ejemplo se usa un objeto task_group para imprimir los valores de dos objetos point en paralelo.La función de trabajo print_point imprime los valores de un objeto point en la consola.La función de trabajo produce una excepción si el valor de entrada es NULL.El runtime almacena esta excepción y calcula las referencias en el contexto que llama a 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;
}
}
Este ejemplo produce el siguiente resultado.
X = 15, Y = 30
Caught exception: point is NULL.
Para obtener un ejemplo completo donde se usa el control de excepciones en un grupo de tareas, vea Cómo: Usar el control de excepciones para interrumpir un bucle Parallel.
[parte superior]
Excepciones que produce el runtime
Una excepción puede ser el resultado de una llamada al runtime.La mayoría de los tipos de excepción, salvo concurrency::task_canceled y de concurrency::operation_timed_out, indican un error de programación.Normalmente, estos errores son irrecuperables y, por consiguiente, no se deben detectar ni administrar en el código de aplicación.Sugerimos sólo detectar o controlar los errores irrecuperables en el código de aplicación si es necesario diagnosticar errores de programación.Sin embargo, conocer los tipos de excepciones que define el runtime puede ayudar a diagnosticar los errores de programación.
El mecanismo de control de excepciones es el mismo para las excepciones que produce el runtime y las excepciones que producen las funciones de trabajo.Por ejemplo, la función de concurrency::receive produce operation_timed_out cuando no recibe un mensaje en el período de tiempo especificado.Si receive produce una excepción en una función de trabajo que se pasa a un grupo de tareas, el runtime almacena esa excepción y calcula las referencias en el contexto que llama a task_group::wait, structured_task_group::wait, task_group::run_and_wait o structured_task_group::run_and_wait.
El ejemplo siguiente se usa el algoritmo de concurrency::parallel_invoke para ejecutar dos tareas en paralelo.La primera tarea espera cinco segundos y, a continuación, envía un mensaje a un búfer de mensajes.La segunda tarea usa la función receive para esperar tres segundos a recibir un mensaje desde el mismo búfer de mensajes.La función receive produce la excepción operation_timed_out si no recibe el mensaje en dicho período de tiempo.
// 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;
}
}
Este ejemplo produce el siguiente resultado.
The operation timed out.
Para evitar la terminación anómala de la aplicación, asegúrese de que el código controla las excepciones cuando llama al runtime.Controle también las excepciones al llamar a código externo que use el Runtime de simultaneidad, por ejemplo, una biblioteca de otro fabricante.
[parte superior]
Varias excepciones
Si una tarea o algoritmo paralelo recibe varias excepciones, el runtime calcula las referencias sólo de una de esas excepciones en el contexto de la llamada.El runtime no garantiza para qué excepción se calculan las referencias.
En el ejemplo siguiente se usa el algoritmo parallel_for para imprimir números en la consola.Produce una excepción si el valor de entrada es menor que un determinado valor mínimo o mayor que un determinado valor máximo.En este ejemplo, varias funciones de trabajo pueden producir una excepción.
// 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 continuación, se muestra la salida de este ejemplo.
8
2
9
3
10
4
5
6
7
Caught exception: -5: the value is less than the minimum.
[parte superior]
Cancelación
No todas las excepciones indican un error.Por ejemplo, un algoritmo de búsqueda podría utilizar el control de excepciones para detener su tarea asociada cuando encuentra el resultado.Para obtener más información sobre cómo usar los mecanismos de cancelación en el código, vea Cancelación en la biblioteca PPL.
[parte superior]
Tareas ligeras
Una tarea ligera es una tarea que se programa directamente de un objeto de concurrency::Scheduler .Las tareas ligeras implican una menor sobrecarga que las tareas ordinarias.Sin embargo, el runtime no detecta las excepciones producidas por las tareas ligeras.En su lugar, el controlador de excepciones no controladas, que de forma predeterminada finaliza el proceso, detecta la excepción.Por consiguiente, use un mecanismo de control de errores adecuado en su aplicación.Para obtener más información sobre las tareas ligeras, vea Programador de tareas (Runtime de simultaneidad).
[parte superior]
Agentes asincrónicos
Como sucede con las tareas ligeras, el runtime no controla las excepciones producidas por agentes asincrónicos.
El ejemplo siguiente se muestra una manera de controlar excepciones en una clase derivada de concurrency::agent.En este ejemplo se define la clase points_agent.El método points_agent::run lee los objetos point del búfer de mensajes y los imprime en la consola.El método run produce una excepción si recibe un puntero NULL.
El método run rodea todo el trabajo en un bloque try-catch.El bloque catch almacena la excepción en un búfer de mensajes.La aplicación comprueba si el agente encontró un error leyendo el búfer cuando el agente finaliza.
// 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;
}
Este ejemplo produce el siguiente resultado.
X: 10 Y: 20
X: 20 Y: 30
error occurred in agent: point must not be NULL
the status of the agent is: done
Dado que el bloque try-catch existe fuera del bucle while, el agente finaliza el procesamiento cuando encuentra el primer error.Si el bloque try-catch estuviese dentro del bucle while, el agente continuaría después de producirse el error.
En este ejemplo se almacenan las excepciones en un búfer de mensajes para que otro componente pueda supervisar los errores del agente mientras se ejecuta.Este ejemplo usa un objeto de concurrency::single_assignment para almacenar el error.Cuando un agente controla varias excepciones, la clase single_assignment almacena únicamente el primer mensaje que se le pasa.Para almacenar solo la última excepción, use la clase de concurrency::overwrite_buffer .Para almacenar todas las excepciones, utilice la clase de concurrency::unbounded_buffer .Para obtener más información acerca de estos bloques de mensajes, vea Bloques de mensajes asincrónicos.
Para obtener más información sobre los agentes asincrónicos, vea Agentes asincrónicos.
[parte superior]
Resumen
[parte superior]
Vea también
Conceptos
Paralelismo de tareas (Runtime de simultaneidad)
Cancelación en la biblioteca PPL
Sugerencia
Precaución