特に、ブロッキング キューを探しています。C++11にそんなものあるの?そうでない場合、他にどのような選択肢がありますか? もう自分でスレッドレベルに行きたくありません。あまりにもエラーが発生しやすい。
6 に答える
Microsoft の Visual C++ チームの Diego Dagum によると:
STL コンテナとそれらがスレッド セーフかどうかについて、よくある質問 (よくある質問の 1 つ) です。
Stephan の言葉をここで引用すると、現実には、バグとしてではなく、機能としてではありません。すべての STL コンテナーのすべてのメンバー関数が内部ロックを取得すると、パフォーマンスが低下します。汎用の高度に再利用可能なライブラリとして、実際には正確さも提供しません。ロックを配置する正しいレベルは、プログラムが何をしているかによって決まります。そういう意味では、個々のメンバー関数はそれほど正しいレベルにはなりません。
並列パターン ライブラリ(PPL) には、要素へのスレッド セーフなアクセスを提供するいくつかのコンテナーが含まれています。
- concurrent_vector クラスは、任意の要素へのランダム アクセスを可能にするシーケンス コンテナー クラスです。これにより、同時実行セーフな追加、要素アクセス、イテレータ アクセス、およびイテレータ トラバーサル操作が可能になります。
- concurrent_queue クラスは、その要素への先入れ先出しアクセスを可能にするシーケンス コンテナー クラスです。いくつか例を挙げると、push や try_pop など、限定された同時実行セーフ操作のセットを有効にします。
ここにいくつかのサンプルがあります。
C++11 は、それ自体では並行コンテナーを提供しません。ただし、ライブラリ オプションがあります。既に述べた PPL の他に、Intel TBB ライブラリを忘れないでください。
queue
、hash_map
、set
およびの同時vector
実装があります。しかし、これはスレッドセーフなコンテナー ライブラリであるだけでなく、標準アルゴリズム (for-loop、reduce、sort など) の並列バージョンも付属しています。
私は、誰もmoveycamel::ConcurrentQueueについて言及していないことに驚いています。かなり長い間使用しており、非常に優れたパフォーマンスを発揮します。その実装がロックフリーであることは明確であり、これによりすぐに驚異的な速度が得られます。それを使用するその他の理由(公式サイトからの引用):
C++ 用の本格的なロックフリー キューはそれほど多くありません。Boostには1つありますが、たとえば、単純な代入演算子と単純なデストラクタを持つオブジェクトに限定されています。Intel の TBB キューはロックフリーではなく、簡単なコンストラクターも必要です。C++ でロックフリー キューを実装する学術論文は多数ありますが、使用可能なソース コードを見つけるのは難しく、さらにテストを行います。
いくつかのベンチマークと比較は、ここ、ここ、およびここで入手できます。
警告: 複数のプロデューサーの場合、ポップされた要素の順序がプッシュされた要素 (@IgorLevicki) の順序と同じであることが保証されていないため、この保証が必要な場合は、他のオプションを探してください。
コンテナのインターフェースは、この目的で設計されたものではありません。彼らが使用するインターフェースの場合、クライアントに見えるロックは、正確さと予測可能な動作を保証しながら、これを達成できる唯一の方法です。また、買収の数が非常に多いため(適切な実装と比較して)、非常に非効率的です。
解決策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
使用して、基になるコレクションを公開できます。そのアプローチが絶対確実であることを意味するのではなく、ただ愚か者に抵抗するだけです。