CAS 方式で TBB が提供する concurrent_hash_map の内容を更新する必要があります。つまり、キーが既に存在する場合、キーに対応する値を調べ、アトミック操作で値を更新します (別のスレッドが同じことを行っているために値が変更された場合、操作は失敗するはずです)。
つまり、insert メソッドに「期待値」を提供し、現在の値が期待値と一致する場合にのみ値を更新します。
TBBのconcurrent_hash_mapでこれを達成する方法はありますか?
どうもありがとう。
CAS 方式で TBB が提供する concurrent_hash_map の内容を更新する必要があります。つまり、キーが既に存在する場合、キーに対応する値を調べ、アトミック操作で値を更新します (別のスレッドが同じことを行っているために値が変更された場合、操作は失敗するはずです)。
つまり、insert メソッドに「期待値」を提供し、現在の値が期待値と一致する場合にのみ値を更新します。
TBBのconcurrent_hash_mapでこれを達成する方法はありますか?
どうもありがとう。
型 Key と T を指定すると、型 T が tbb::atomic がサポートされている型であると仮定して、次のコードで目的が達成されます。
class AtomicValue {
mutable tbb::atomic<T> content;
public:
AtomicValue() {}
AtomicValue( T value ) {content=value;}
bool cas( T value, T comparand ) const {
return content.compare_and_swap(value,comparand)==comparand;
}
};
typedef tbb::concurrent_hash_map<Key,AtomicValue> table;
bool update( table& x, Key key, T value, T comparand ) {
table::const_accessor a;
if( !x.insert(a,table::value_type(key,value) ) ) {
// value is already there
return a->second.cas(value,comparand);
}
return true;
}
注意が必要な部分は、const_accessor を使用して更新を行うことです。通常のアクセサーを使用すると、更新がシリアル化されます。ただし、const_accessor を使用すると、複数のスレッドが同じテーブル エントリに同時にアクセスできます。通常のユースケースでは値の読み取りが必要になるため、「const_accessor」と呼ばれます。ただし、ここでは、コードは CAS を使用して更新を調停します。ラッパー クラス「AtomicValue」を使用すると、const オブジェクトで CAS を実行できます。
tbb::concurrent_unordered_map でも同様の解決策が機能するはずです。これは、非ブロッキングが重要な基準である場合に適している可能性があります。これは、concurrent_unordered_map には非ブロッキング実装があるためです。
さらに良いことに、最新の TBBと、constexpr の C++11 機能とデフォルト/削除されたメンバー関数をサポートするコンパイラがあれば、次のように動作するはずです。
typedef tbb::concurrent_unordered_map<Key,tbb::atomic<T> > table;
bool update( table& x, Key key, T value, T comparand ) {
auto p = x.insert(table::value_type(key,value) );
if( !p.second ) {
// value is already there
return p.first->second.compare_and_swap(value,comparand) == comparand;
}
return true;
}
「g++ -std=c++0x」でコンパイルすると、gcc 4.7 で動作しました。