TBB之concurrent_vector

concurrent_vector是一个动态可增长的数字,当其他线程对其中的元素操作时,可安全push元素到concurrent_vector,对于安全并行增加元素,concurrent_vector有3个方法支持动态数组的使用:push_back, grow_by以及grow_to_at_least。

方法push_back(x)安全把x赋给array,方法grow_by(n)安全赋值n个连续的元素,利用T()来初始化,这2个方法返回一个迭代器,指向第一个元素,每个元素使用T()来初始化,例如,下面程序安全的把C风格的字符串赋给一个共享的vector中:

void Append( concurrent_vector<char>& vector, const char* string ) {
    size_t n = strlen(string)+1;
    std::copy( string, string+n, vector.grow_by(n) );
}

方法grow_to_at_least(n)增加vector到n,当它较短时。

方法size()返回vector的元素数量,它可能包括仍然同步构建的元素,通过方法push_back, grow_by或者grow_to_at_least。这个例子使用std::copy和iterators,而不是strcpy和pointers,因为concurrent_vector里的元素不可能是连续寻址,当concurrent_vector正在增加时,使用iterators是安全的,只要iterators没有遭遇end()。然而,iterator可能引用一个遭受并行建设的元素,你一定要同步建设和访问。
concurrent_vector从来没有移除元素直到array被clear掉。

Clearing is Not Concurrency Safe

注意:当concurrent_vector增加时,对concurrent_vector操作是安全的,clear或者摧毁一个vector不是安全的,如果有其他对concurrent_vector的操作不要调用方法clear()。

Advanced Idiom: Waiting on an Element

有时一个线程一定等待一个元素v[i],这个元素由其他线程同步添加:

  • 等到i<v.size() ,在这之后,v[i]被分配,但是也许没有被构建。
  • 等到v[i]被构建。

一个好的方法实现步骤2是等待这个元素的atomic flag变成non-zero,为了确保flag是zero,需要做:

  • 使用分配zeroed内存的分配器来实例化concurrent_vector,例如tbb::zero_allocator。
  • 让元素的构造函数设置这个flag成non-zero,作为最后的行为。

下面是一个例子,vector元素是一个atomic指针,假设添加到vector的指针是non-NULL:

#include tbb/compat/thread
#include tbb/tbb_allocator.h // zero_allocator defined here
#include tbb/atomic.h
#include tbb/concurrent_vector.h
using namespace tbb;

typedef concurrent_vector<atomic<Foo*>, zero_allocator<atomic<Foo*> > >
FooVector;

Foo* FetchElement( const FooVector& v, size_t i ) {
    // Wait for ith element to be allocated
    while( i>=v.size() )
        std::this_thread::yield();
    // Wait for ith element to be constructed
    while( v[i]==NULL )
        std::this_thread::yield();
    return v[i];
}

通常,这个flag一定是atomic类型,目的确保适当的内容一致性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章