0

私はC++でマルチスレッドプログラムを作成しており、メインスレッドでは、他のスレッドがパッケージを別のキューに入れるのを待っています。パッケージの種類とそれらがどのスレッドから発生したかによって異なります。

キューは、本来あるべきミューテックスによって保護されています。

しかし、私の主な目的は、やりたくないことです。

while(true)
if(!queue1->empty)
{
     do stuff
}
if(!queue2->empty)
{
     do stuff
}
etc

したがって、条件変数を使用して、何かが変更されたことをメインに通知する必要があります。現在、1つの条件変数でしかブロックできないため、これらすべてのスレッドで同じ条件変数と付随するミューテックスを使用する必要があります。今、私はこのミューテックスを実際に使用してすべてのスレッドをロックしたくありません。1つのスレッドがキューに書き込んでいるときに、別のスレッドがまったく異なるキューに書き込むことができないという意味ではありません。そのため、キューごとに個別のミューテックスを使用します。しかし、条件変数に付属するこの追加のミューテックスをどのように使用すればよいでしょうか。

stdと非常によく似た、ブーストを使用した2つのスレッドと1つのキューでどのように行われるか。 http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

template<typename Data>
class concurrent_queue
{
    private:
    boost::condition_variable the_condition_variable;  
    public:
    void wait_for_data()
    {
        boost::mutex::scoped_lock lock(the_mutex);
        while(the_queue.empty())
        {
             the_condition_variable.wait(lock);
        }
    }
    void push(Data const& data)
    {
         boost::mutex::scoped_lock lock(the_mutex);
         bool const was_empty=the_queue.empty();
         the_queue.push(data);
         if(was_empty)
         {
             the_condition_variable.notify_one();
         }
     }
     // rest as before
 };

では、これをどのように解決しますか?

4

3 に答える 3

3

私はあなたの問題の鍵はここにあると思います:

Now I dont want to really use this mutex to lock all my threads. It doesn't mean that when 1 thread is writing to a queue, another cant write to a totally different queue. So I use seperate mutexes for each queue.

なんで?なぜなら:

... packages come in relatively slow. And queues are empty most of the time

あなたは、あなたが言及した使用シナリオで1つのキューが実際に機能したので、実際にはおそらくそれを必要としないのに、あなたが必要だと思った何かのために自分を隅に追いやったように思えます。

私は、1つのキューから始めて、それがどこまで到達するかを確認すると思います。次に、1つのミューテックスで待機しているスレッドが実際に多いという制限に遭遇した場合、問題に関するより多くの情報が得られるため、問題をより適切に解決できるようになります。

本質的に、この問題に直面している理由は、時期尚早の設計最適化と、それを修正する方法は、今すぐ設計を遡って変更することだと思います。

于 2013-03-21T23:27:37.160 に答える
2

作業を行っているすべてのキューの最上位(場合によっては循環)キューを作成します。

このキューは単一のミューテックスで保護でき、空から非空に変化したときにのみ通知する必要があるcondvarを持つことができます。

これで、個々のキューはそれぞれ独自のミューテックスを持つことができ、空から非空に変更されたときにのみ、共有/トップレベルのキュー(およびそのミューテックス)に触れる必要があります。

いくつかの詳細は、スレッドが空でない各キューからフロントアイテムのみを順番に取得するか、キュー全体を順番に消費するかによって異なりますが、アイデアはそこにあります。


空でない状態から空でない状態(ただしサイズが大きくなる)に移行する場合も、トップレベルのキューに渡す必要がありますか?

私が言ったように、それはあなたがそれらをどのように消費するかに依存します。キューに何かが含まれるたびに、次のようにします。

  1. (すでにトップレベルのロックがあります。これにより、このキューに何かが含まれていることがわかります)
  2. キューをロックする
  3. キューの内容をローカルの作業コピーと交換します
  4. トップレベルのキューからキューを削除します
  5. キューのロックを解除する

その場合、作業キューは常に空ではないために最上位キューにある空であるためキューにありません。

これを行わず、空でない各キューからフロント要素をプルするだけの場合は、考慮すべき状態がさらにあります。


次の場合に注意してください

...パッケージは比較的遅いです。そして、キューはほとんどの場合空です

多くの競合を引き起こすのに十分なアクティビティがないため、おそらく1つのキューだけを持つことができます。これは物事を非常に単純化します。

于 2013-03-21T22:04:46.867 に答える
0

@Carleetoと@Uselessの両方が良い答えを出しました。コンシューマーは1つしかないため、1つのキューで最高のパフォーマンスが得られます。常に動作している単一のコンシューマーよりも高いスループットを得ることができないため、プロデューサーではなく、単一のコンシューマーのロックオーバーヘッドを最小限に抑えることを目的とする必要があります。これを行うには、プロデューサーに単一のミューテックスを使用して単一の条件変数(キューが空でないことを示す)を待機させます。

これが、パラメトリック多型を行う方法です。完全な型安全性、キャストなし、親クラスの単一の仮想関数のみ:

class ParentType {
public:
  virtual void do_work(...[params]...)=0;
  virtual ~ParentType() {}
};

class ChildType1 : public ParentType {
private:
  // all my private variables and functions
public:
  virtual void do_work(...[params]...) {
    // call private functions and use private variables from ChildType1
  }
};

class ChildType2: public ParentType {
private:
  // completely different private variables and functions
public:
  virtual void do-work(...[params]...) {
    // call private functions and use private variables from ChildType2
  }
};
于 2013-03-26T02:50:35.650 に答える