windows C++ 并行编程-并发的异常处理(三)
并发运行时使用 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 类。