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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章