Intel TBB 提供高併發的容器類,Windows或者Linux線程能使用這些容器類或者和基於task編程相結合(TBB)。
一個併發容器允許多線程同時對容器訪問和更改條例,典型的C++STL容器類不允許 併發更新,嘗試並行更改他們引起惡化容器。STL容器能使用互斥鎖來包裝,目的讓他們安全訪問,通過只讓一個線程同時操作一個容器,但是這種方式消除了並行,限制了並行提速。
Intel TBB提供的容器有更高水準的併發性,通過以下方法:
- Fine-grained locking:只通過鎖需要鎖的部分,在容器上實現多線程操作,一旦多線程訪問不同部分,他們能同步進行。
- Lock-free techniques:改正其他干擾線程的影響。
注意高併發容器是有代價的,他們典型的有更高的消耗比STL容器,對高併發容器操作比STL容器消耗更多,因此,當使用高併發容器比STL容器提速的時候,他們是優於串行的性能。
concurrent_hash_map
下面的例子建立一個concurrent_hash_map,這個Key是字符串,對應的數據是矩陣Data中字符串出現的次數。
#include "tbb/concurrent_hash_map.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#include <string>
using namespace tbb;
using namespace std;
// Structure that defines hashing and comparison operations for user's
type.
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<string,int,MyHashCompare> StringTable;
// Function object for counting occurrences of strings.
struct Tally {
StringTable& table;
Tally( StringTable& table_ ) : table(table_) {}
void operator()( const blocked_range<string*> 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 table
parallel_for( blocked_range<string*>( Data, Data+N, 1000 ),
Tally(table) );
// Display the occurrences
for( StringTable::iterator i=table.begin(); i!=table.end(); ++i )
printf("%s %d\n",i->first.c_str(),i->second);
}
方法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上的訪問結束。