并发运行时使用 C++ 异常处理来传达多种错误。 这些错误包括:无效使用运行时、无法获取资源等运行时错误,以及你提供给任务和任务组的工作函数中发生的错误。 当任务或任务组引发异常时,运行时会保存该异常并将其编组到等待任务或任务组完成的上下文。 对于轻量级任务和代理等组件,运行时不会为你管理异常。 在这些情况下,你必须实现自己的异常处理机制。 本系列中描述运行时如何处理任务、任务组、轻量级任务和异步代理引发的异常,以及如何在应用程序中响应异常。
多个异常
如果任务或并行算法接收到多个异常,则运行时仅将其中一个异常封送到调用上下文。 运行时不保证它会封送哪个异常。
以下示例使用 parallel_for 算法将数字打印到控制台。 如果输入值小于某个最小值或大于某个最大值,则会引发异常。 在此示例中,多个工作函数可以引发异常。
// 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;
}
}
输出为:
8293104567Caught exception: -5: the value is less than the minimum.
轻量级任务
轻量级任务是直接从 concurrency::Scheduler 对象计划的任务。 轻量级任务的开销比普通任务少。 但是,运行时不会捕获轻量级任务引发的异常。 相反,异常将被未经处理的异常处理程序捕获,默认情况下会终止进程。 因此,在你的应用程序中使用适当的错误处理机制。
异步代理
与轻量级任务一样,运行时不管理由异步代理引发的异常。
以下示例显示了一种处理派生自 concurrency::agent 的类中的异常的方法。 这个例子定义了 points_agent 类。 points_agent::run 方法从消息缓冲区中读取 point 对象并将它们打印到控制台。 如果 run 方法接收到 NULL 指针,则会引发异常。
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 类。