本文共 2756 字,大约阅读时间需要 9 分钟。
Intel TBB 提供高并发的容器类,Windows或者Linux线程能使用这些容器类或者和基于task编程相结合(TBB)。
一个并发容器允许多线程同时对容器访问和更改条例,典型的C++STL容器类不允许 并发更新,尝试并行更改他们引起恶化容器。STL容器能使用互斥锁来包装,目的让他们安全访问,通过只让一个线程同时操作一个容器,但是这种方式消除了并行,限制了并行提速。
Intel TBB提供的容器有更高水准的并发性,通过以下方法:
注意高并发容器是有代价的,他们典型的有更高的消耗比STL容器,对高并发容器操作比STL容器消耗更多,因此,当使用高并发容器比STL容器提速的时候,他们是优于串行的性能。
concurrenthashmap<Key,T,HashCompare> 是一个hash表,允许并行访问,表是一个从Key到类型T的映射,类型HashCompare定义怎样hash一个Key和怎样比较2个Key。
下面的例子建立一个concurrent_hash_map,这个Key是字符串,对应的数据是矩阵Data中字符串出现的次数。
#include "tbb/concurrent_hash_map.h"#include "tbb/blocked_range.h"#include "tbb/parallel_for.h"#includeusing namespace tbb;using namespace std;// Structure that defines hashing and comparison operations for user'stype.struct MyHashCompare { static size_t hash( const string& x ) { size_t h = 0; for( const char* s = x.c_str(); *s; ++s ) h = (h*17)^*s; return h; } //! True if strings are equal static bool equal( const string& x, const string& y ) { return x==y; }};// A concurrent hash table that maps strings to ints.typedef concurrent_hash_map StringTable;// Function object for counting occurrences of strings.struct Tally { StringTable& table; Tally( StringTable& table_ ) : table(table_) {} void operator()( const blocked_range range ) const { for( string* p=range.begin(); p!=range.end(); ++p ) { StringTable::accessor a; table.insert( a, *p ); a->second += 1; } }};const size_t N = 1000000;string Data[N];void CountOccurrences() {// Construct empty table.StringTable table;// Put occurrences into the tableparallel_for( blocked_range ( Data, Data+N, 1000 ), Tally(table) );// Display the occurrencesfor( StringTable::iterator i=table.begin(); i!=table.end(); ++i ) printf("%s %d\n",i->first.c_str(),i->second);}
concurrenthashmap 扮演一个类型 std::pair<constKey,T> 元素的容器,当访问一个容器元素时,你对更新它或者读取它感兴趣,模板类concurrent_hash_map支持这2个目的,对应类accessor和const_accessor,其担当智能指针,accessor表示更新(write)访问,一旦它指向一个元素,所有其他尝试寻找这个表的key都会被阻止,直到accessor被完成。const_accessors是类似的,除了只表示读访问,多const_accessors能同时指向相同的元素,在频繁的读但是很少更新的情况下,这个特点极大的改善并行情形。
方法find和insert采用accessor或者const_accessor作为一个参数,告诉concurrent_hash_map是否你要求更新或者只读访问,一旦这个方法返回,直到访问结束,accessor或者const_accessor才会被销毁,因为对元素的访问阻止其他线程,所以尽可能的缩减accessor或者const_accessor生命周期,为了这么做,我们在内部块中声明它,尽可能在块结束前释放这个accessor,下面的例子是重写循环体,使用release结束accessor的生命周期:
StringTable accessor a;for( string* p=range.begin(); p!=range.end(); ++p ) { table.insert( a, *p ); a->second += 1; a.release();}
方法remove(key)也能同时操作,它隐式请求写访问,因此,在删除这个Key之前,它等待其他现存key上的访问结束。
转载地址:http://qimci.baihongyu.com/