Intel TBB支持exceptions和cacellation。当Intel TBB算法里的代码抛出exception时,会发生下面的事情:
- exception被捕获,在算法里更远的exceptions被忽略。
- 算法被取消,未实现的迭代不被执行,如果内部嵌套Intel TBB的并行程序,它会被取消。
- 一旦算法所有部分停止,那么调用这个算法的线程会抛出一个exception。
#include "tbb/tbb.h"
#include <vector>
#include <iostream>
using namespace tbb;
using namespace std;
vector<int> Data;
struct Update {
void operator()( const blocked_range<int>& r ) const {
for( int i=r.begin(); i!=r.end(); ++i )
Data.at(i) += 1;
}
};
int main() {
Data.resize(1000);
try {
parallel_for( blocked_range<int>(0, 2000), Update());
} catch( captured_exception& ex ) {
cout << "captured_exception: " << ex.what() << endl;
} catch( out_of_range& ex ) {
cout << "out_of_range: " << ex.what() << endl;
}
return 0;
}
parallel_for尝试迭代vector的2000个元素,但是里面只有1000个元素,因此在执行这个算法期间,表达式Data.at(i)抛出一个std::out_of_range,当exception发生时,算法取消并且在调用parallel_for时抛出一个exception。
Cancellation Without An Exception
为了取消算法,但是不抛出异常,使用表达式task::self().cancel_group_execution()。task::self参考Intel TBB task,调用cancel_group_execution()取消task_group_context里所有的任务,下一部将详细解释,如果它确实引起cancellation,那么方法返回true,如果task_group_context已经取消了,它返回false。
下面的例子展示怎样使用task::self().cancel_group_execution():
#include "tbb/tbb.h"
#include <vector>
#include <iostream>
using namespace tbb;
using namespace std;
vector<int> Data;
struct Update {
void operator()( const blocked_range<int>& r ) const {
for( int i=r.begin(); i!=r.end(); ++i )
if( i<Data.size() ) {
++Data[i];
} else {
// Cancel related tasks.
if( task::self().cancel_group_execution() )
cout << "Index " << i << " caused cancellation\n";
return;
}
}
};
int main() {
Data.resize(1000);
parallel_for( blocked_range<int>(0, 2000), Update());
return 0;
}
Cancellation and Nested Parallelism
目前讨论的都是假设没有内部嵌套parallelism的情况,跳过task_group_context的细节,这部分介绍这2点。
Intel TBB算法通过创建一系列的task对象来执行的,默认情况下,这些task和task_group_context关联,且task_group_context由算法创建,取消一个task_group_context相当于取消所有子task_group_context对象。
exceptions向前传播,cancellation向后传播。例如,考虑下图的树状图,设想每个节点表示一个算法和一个task_group_context。
假设C节点上的算法抛出exception并且没有节点捕获这个exception,Intel TBB向前传播exception,向下取消相关子树的exception,如下:
如果你的代码在任何级别上捕获这个exception,那么Intel TBB不会向更远的方向传播。例如,一个exception不会逃离出一个parallel_for,那么parallel_for不会引起其他迭代的cancellation。
为了阻止向下传播cancellation,在stack上构造一个”isolated”task_group_context,并且显示的传递给这个算法,如下:
#include "tbb/tbb.h"
bool Data[1000][1000];
int main() {
try {
parallel_for( 0, 1000, 1,
[]( int i ) {
task_group_context root(task_group_context::isolated);
parallel_for( 0, 1000, 1,
[]( int j ) {
Data[i][j] = true;
},
root);
throw "oops";
});
} catch(...) {
}
return 0;
}
这个例子执行2个parallel循环,外部的循环在i上进行,内部的循环在j上进行,isolated的task_group_context root创建保护内部循环免受cacellation的干扰,当exception传播到外部循环,任何未使用的迭代会取消,但是内部循环不会取消,因此当程序结束时,Data每一行可能是不同的,但是在同一行里,元素会是全部的false或者true,而不是一个混合。