特に、ブロッキングキューを探しています。 C++ 11にはそのようなことがありますか?そうでない場合、他のオプションは何ですか?私は本当に自分自身でスレッドレベルにまで下がりたくありません。エラーが発生しやすい方法です。
MicrosoftのVisual C++チームのDiego Dagumによる :
よくある質問(多くの質問の1つ)は、STLコンテナーと、それらがスレッドセーフかどうかに関するものです。
ここでステファンの言葉を借りると、現実はバグとしてではなく機能としてであるということです。すべてのSTLコンテナのすべてのメンバー機能が内部ロックを取得すると、パフォーマンスが消滅します。汎用性の高い再利用可能なライブラリであるため、実際には正確性も提供されません。ロックを配置するための正しいレベルは、プログラムの実行内容によって決まります。その意味で、個々のメンバー機能はそのような正しいレベルになる傾向はありません。
The Parallel Patterns Library (PPL)には、要素へのスレッドセーフアクセスを提供するいくつかのコンテナが含まれています。
いくつかのサンプル ここ 。
C++ 11は、単独では同時コンテナーを提供しません。ただし、ライブラリオプションがあります。すでに述べたPPLのほかに、Intel TBBライブラリも忘れないでください。
同時にqueue
、hash_map
、set
およびvector
の実装。しかし、スレッドセーフなコンテナライブラリだけでなく、標準バージョンの並列バージョン(for-loop、reduce、sortなど)も付属しています。
moodycamel :: ConcurrentQueue に誰も言及していないことに驚いています。私たちはかなり長い間それを使用してきましたが、非常にうまく機能しています。その実装がロックフリーであることは明確であり、それは即座に非常に速い速度をもたらします。それを使用する他の理由(公式サイトから引用):
C++用の本格的なロックフリーキューはそれほど多くありません。 Boostには1つありますが、たとえば、単純な代入演算子と単純なデストラクタを持つオブジェクトに限定されます。 IntelのTBBキューはロックフリーではなく、簡単なコンストラクタも必要です。 C++でロックフリーキューを実装する学術論文は多数ありますが、使用可能なソースコードを見つけるのは難しく、さらにテストする必要があります。
コンテナのインターフェースは、単にこの目的で設計されたものではありません。クライアントが使用するインターフェイスの場合、クライアントに見えるロックは、正確さと予測可能な動作を保証しながらこれを達成できる唯一の方法です。また、買収の数が非常に多くなるため、非常に非効率的です(適切な実装と比較して)。
ソリューション1
値渡し(該当する場合)。
ソリューション2
スコープロックを保持しながらコンテナーを渡すために使用できる単純なボルトオン実装のコレクションを作成します(疑似c ++を考慮してください)。
template <typename TCollection>
class t_locked_collection {
public:
t_locked_collection(TCollection& inCollection, t_lock& lock) : collection(inCollection), d_lock(lock), d_nocopy() {
}
TCollection& collection;
// your convenience stuff
private:
t_scope_lock d_lock;
t_nocopy d_nocopy;
};
次に、呼び出し側がロックとコレクションをペアリングし、インターフェースを更新して、必要に応じてコンテナタイプを使用(パスバイ)します。それはただの貧乏人のクラス拡張です。
このロックされたコンテナは1つの簡単な例であり、他にもいくつかのバリエーションがあります。これは、ロックされたメソッドほど(構文的に)透過的ではない場合でも、プログラムに最適な粒度レベルを実際に使用できるため、私が選択したルートです。既存のプログラムを適応させるのも比較的簡単です。少なくとも、内部ロックのあるコレクションとは異なり、予測可能な方法で動作します。
別のバリアントは次のとおりです。
template <typename TCollection>
class t_lockable_collection {
public:
// ...
private:
TCollection d_collection;
t_mutex d_mutex;
};
// example:
typedef t_lockable_collection<std::vector<int> > t_lockable_int_vector;
...ここで、t_locked_collection
は、基になるコレクションを公開するために使用できます。そのアプローチがだまされないことを意味するのではなく、だまされにくいだけです。
C++ 11には同時コンテナはありません。
ただし、次のヘッダークラスは、std :: dequeを使用して、キュー、スタック、および優先度のコンテナを同時に提供します。
BlockingCollection は、.NET BlockingCollectionクラスをモデルにしたC++ 11スレッドセーフコレクションクラスです。
並行して順序付けられていないマップ名前空間同時実行の私のバージョン{
template<typename T,typename T1>
class unordered_bucket: private std::unordered_map<T,T1>
{
mutable std::recursive_mutex m_mutex;
public:
T1 &operator [](T a)
{
std::lock_guard<std::recursive_mutex> l(m_mutex);
return std::unordered_map<T,T1>::operator [](a);
}
size_t size() const noexcept {
std::lock_guard<std::recursive_mutex> l(m_mutex);
return std::unordered_map<T,T1>::size();
}
vector<pair<T,T1>> toVector() const
{
std::lock_guard<std::recursive_mutex> l(m_mutex);
vector<pair<T,T1>> ret;
for(const pair<T,T1> &p:*this)
{
ret.Push_back(p);
}
return ret;
}
bool find(const T &t) const
{
std::lock_guard<std::recursive_mutex> l(m_mutex);
if(this->std::unordered_map<T,T1>::find(t) == this->end())
return false; //not found
return true;
}
void erase()
{
std::lock_guard<std::recursive_mutex> l(m_mutex);
this->unordered_map<T,T1>::erase(this->begin(),this->end());
}
void erase(const T &t)
{
std::lock_guard<std::recursive_mutex> l(m_mutex);
this->unordered_map<T,T1>::erase(t);
}
};
#define BUCKETCOUNT 10
template<typename T,typename T1>
class ConcurrentMap
{
std::vector<unordered_bucket<T,T1>> m_v;
public:
ConcurrentMap():m_v(BUCKETCOUNT){} //using 10 buckets
T1 &operator [](T a)
{
std::hash<T> h;
return m_v[h(a)%BUCKETCOUNT][a];
}
size_t size() const noexcept {
size_t cnt=0;
for(const unordered_bucket<T,T1> &ub:m_v)
cnt=cnt+ub.size();
return cnt;
}
vector<pair<T,T1>> toVector() const
{
vector<pair<T,T1>> ret;
for(const unordered_bucket<T,T1> &u:m_v)
{
const vector<pair<T,T1>> &data=u.toVector();
ret.insert(ret.end(),data.begin(),data.end());
}
return ret;
}
bool find(const T &t) const
{
for(const unordered_bucket<T,T1> &u:m_v)
if(true == u.find(t))
return true;
return false;
}
void erase()
{
for(unordered_bucket<T,T1> &u:m_v)
u.erase();
}
void erase(const T &t)
{
std::hash<T> h;
unordered_bucket<T,T1> &ub = m_v[h(t)%BUCKETCOUNT];
ub.erase(t);
}
};
}