カットの下の詳細。
問題の声明。
ライター(以降、ライター)とリーダー(リーダー)の2つのストリームがあります。 ロック(ミューテックスまたはその他のブロッキングメソッド)を使用せずに、ライターからリーダーにメッセージを送信するにはキューが必要です。
少し考えて、次の解決策を得ました。スレッドが互いに干渉しないように、2つの部分からなるキューを使用する必要があります。 1つの部分はライターによって使用され、他の部分はリーダーによって使用されます。 アルゴリズムは次のとおりです。
- ライターは、キューの自分の部分にメッセージを書き込み、リーダーのキューをチェックします。 空の場合、ライターの順番がリーダーに渡され、ライターの新しいキューが作成されます。
- リーダーはキューの自分の部分をチェックし、空でない場合はそこから値を取得します。
RSDNでの実装をレイアウトしました(リンクは記事の最後にあります)が、いくつかの議論の後、このトピックは収まりました。 そして、ここで私のベストプラクティスを共有したいと思います。
実際のコード:
#define LOCK_FREE_QUEUE2_USE_FLUSH 1 template <class TYPE> class LockFreeQueue2 { public: LockFreeQueue2(); ~LockFreeQueue2(); // void Enqueue(TYPE* data); // bool Dequeue(volatile TYPE*& data); #if LOCK_FREE_QUEUE2_USE_FLUSH // . bool Flush(); #else // void SetWriterFinished(); #endif private: volatile TYPE *readerTop; // TYPE *writerTop, *writerBottom; // #if LOCK_FREE_QUEUE2_USE_FLUSH == 0 volatile bool isWriterFinished; #endif }; template<class TYPE> LockFreeQueue2<TYPE>::LockFreeQueue2() { readerTop = writerTop = writerBottom = NULL; #if LOCK_FREE_QUEUE2_USE_FLUSH == 0 isWriterFinished = false; #endif } template<class TYPE> LockFreeQueue2<TYPE>::~LockFreeQueue2() { } template<class TYPE> void LockFreeQueue2<TYPE>::Enqueue(TYPE* data) { data->next = NULL; if (writerTop) // , { writerBottom->next = data; writerBottom = data; } else // { writerTop = writerBottom = data; } if (!readerTop) // , { readerTop = writerTop; writerTop = NULL; } } template<class TYPE> bool LockFreeQueue2<TYPE>::Dequeue(volatile TYPE*& data) { if (!readerTop) // { #if LOCK_FREE_QUEUE2_USE_FLUSH == 0 if (isWriterFinished && writerTop) // , { readerTop = writerTop; writerTop = NULL; } else #endif { return false; // } } // - data = readerTop; readerTop = readerTop->next; return true; } #if LOCK_FREE_QUEUE2_USE_FLUSH template<class TYPE> bool LockFreeQueue2<TYPE>::Flush() { if (!writerTop) return true; if (!readerTop) // { readerTop = writerTop; return true; } return false; } #else template<class TYPE> void LockFreeQueue2<TYPE>::SetWriterFinished() { isWriterFinished = true; } #endif
LOCK_FREE_QUEUE2_USE_FLUSH 定義は 、ライターの作業の最後に2つの異なるタイプのキュー動作をコンパイルするために使用されます。 値が1の場合、ライターはチャットキューをリーダーにスローするまでFlushメソッドを呼び出す必要があります。 値が0の場合、Writerは単純にisWriterFinished変数の値をtrueに設定して停止します。 読者自身が最後に残りを拾います。
また、ライターが永遠に待たないように、リーダーが作業を終了するにはフラグが必要なようです。
この実装には、いくつかの機能を追加できます。 たとえば、アイテムカウンターを追加できます。 また、カウンター変数への同時アクセスが発生しないように、キューの各部分ごとに分離する必要があります。 これらのカウンターの合計は、キュー内の要素の総数になります。
また、リーダーにイベントを追加することもできます。これに応じて、ライターが彼に自分の役割を与えたときに目覚めます。 しかし、すべてがそれほど単純ではなく、同時アクセスの問題が発生する可能性があります。
このキューは、1つのライターと多くのリーダーの状況には使用できませんが、ルーター(ディスパッチャー)と各リーダーに1つのキューを使用できます。 ルーターは、メッセージをキューに追加するリーダーを決定する必要があります。 最も単純な場合、この基準はキュー内の要素の最小数です。
記事を公開する前に、私はこのトピックについて少しグーグルで調べましたが、似たようなものは見つかりませんでした。 たぶん見た目が悪かったので、自転車を発明した場合や、実装に誤りを見つけたらコメントに書いてください。
RSDNのディスカッションへのリンク 。