TBB之Exceptions and Cancellation

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

發佈了27 篇原創文章 · 獲贊 26 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章