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類型,目的確保適當的內容一致性。

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