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,而不是一個混合。