web-dev-qa-db-ja.com

C ++ 11に並行コンテナはありますか?

特に、ブロッキングキューを探しています。 C++ 11にはそのようなことがありますか?そうでない場合、他のオプションは何ですか?私は本当に自分自身でスレッドレベルにまで下がりたくありません。エラーが発生しやすい方法です。

53
fredoverflow

MicrosoftのVisual C++チームのDiego Dagumによる

よくある質問(多くの質問の1つ)は、STLコンテナーと、それらがスレッドセーフかどうかに関するものです。

ここでステファンの言葉を借りると、現実はバグとしてではなく機能としてであるということです。すべてのSTLコンテナのすべてのメンバー機能が内部ロックを取得すると、パフォーマンスが消滅します。汎用性の高い再利用可能なライブラリであるため、実際には正確性も提供されません。ロックを配置するための正しいレベルは、プログラムの実行内容によって決まります。その意味で、個々のメンバー機能はそのような正しいレベルになる傾向はありません。

The Parallel Patterns Library (PPL)には、要素へのスレッドセーフアクセスを提供するいくつかのコンテナが含まれています。

  • concurrent_vector Class は、任意の要素へのランダムアクセスを許可するシーケンスコンテナークラスです。これにより、同時実行に対して安全な追加、要素アクセス、反復子アクセス、反復子走査操作が可能になります。
  • concurrent_queue Class は、要素への先入れ先出しアクセスを可能にするシーケンスコンテナクラスです。いくつか例を挙げると、Pushやtry_popなど、限られた同時実行セーフ操作のセットを有効にします。

いくつかのサンプル ここ

また興味深い: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

36
Lior Kogan

C++ 11は、単独では同時コンテナーを提供しません。ただし、ライブラリオプションがあります。すでに述べたPPLのほかに、Intel TBBライブラリも忘れないでください。

同時にqueuehash_mapsetおよびvectorの実装。しかし、スレッドセーフなコンテナライブラリだけでなく、標準バージョンの並列バージョン(for-loop、reduce、sortなど)も付属しています。

Intel TBB Webサイト

10
Lars K.

moodycamel :: ConcurrentQueue に誰も言及していないことに驚いています。私たちはかなり長い間それを使用してきましたが、非常にうまく機能しています。その実装がロックフリーであることは明確であり、それは即座に非常に速い速度をもたらします。それを使用する他の理由(公式サイトから引用):

C++用の本格的なロックフリーキューはそれほど多くありません。 Boostには1つありますが、たとえば、単純な代入演算子と単純なデストラクタを持つオブジェクトに限定されます。 IntelのTBBキューはロックフリーではなく、簡単なコンストラクタも必要です。 C++でロックフリーキューを実装する学術論文は多数ありますが、使用可能なソースコードを見つけるのは難しく、さらにテストする必要があります。

いくつかのベンチマークと比較が利用可能です herehere および here

7
Miljen Mikic

コンテナのインターフェースは、単にこの目的で設計されたものではありません。クライアントが使用するインターフェイスの場合、クライアントに見えるロックは、正確さと予測可能な動作を保証しながらこれを達成できる唯一の方法です。また、買収の数が非常に多くなるため、非常に非効率的です(適切な実装と比較して)。

ソリューション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は、基になるコレクションを公開するために使用できます。そのアプローチがだまされないことを意味するのではなく、だまされにくいだけです。

1
justin

C++ 11には同時コンテナはありません。

ただし、次のヘッダークラ​​スは、std :: dequeを使用して、キュー、スタック、および優先度のコンテナを同時に提供します。

BlockingCollection は、.NET BlockingCollectionクラスをモデルにしたC++ 11スレッドセーフコレクションクラスです。

0
gm127

並行して順序付けられていないマップ名前空間同時実行の私のバージョン{

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);
}
};
}
0