1

マルチスレッドで問題が発生しました。マルチスレッドのモデルは 1 プロデューサー - N コンシューマーです。

Producer はデータ (それぞれ約 200 バイトの文字データ) を生成し、それを固定サイズのキャッシュ (つまり 2Mil) に入れます。データはすべてのスレッドに関連しているわけではありません。フィルター (構成済み) を適用し、生成されたデータに適格なスレッドがないかどうかを判別します。

プロデューサーは、データへのポインターを適格なスレッドのキューにプッシュします (データのコピーを避けるため、データへのポインターのみ)。スレッドはそれをデキューし、TCP/IP 経由でクライアントに送信します。

問題: データへのポインタのみが複数のスレッドに渡されるため、キャッシュがいっぱいになると、プロデュースは最初のアイテム (古いアイテム) を削除したいと考えています。スレッドがまだデータを参照している可能性があります。

実現可能な方法 : アトミック粒度を使用します. プロデューサーが適格なスレッドの数を決定すると, カウンターとスレッド ID のリストを更新できます.

class InUseCounter
{
    int           m_count;
    set<thread_t> m_in_use_threads;
    Mutex         m_mutex;
    Condition     m_cond;

public:
    // This constructor used by Producer
    InUseCounter(int count, set<thread_t> tlist)
    {
        m_count          = count;
        m_in_use_threads = tlist;
    } 

    // This function is called by each threads
    // When they are done with the data, 
    // Informing that I no longer use the reference to the data.
    void decrement(thread_t tid)
    {
        Gaurd<Mutex> lock(m_mutex);
        --m_count;
        m_in_use_threads.erease(tid);
    }

    int get_count() const { return m_count; }
};

マスターチャッシュ

map<seqnum, Data>
              |
              v
             pair<CharData, InUseCounter>

プロデューサーがカウンターをチェックする要素を削除すると、0 より大きい場合、m_in_use_threads セット内のスレッドへの参照を解放するアクションを送信します。

質問

  1. マスター キャッシュに 2Mil のレコードがある場合、同じ数の InUseCounter が存在するため、Mutex 変数は、1 つのプロセスで 2Mil のミューテックス変数を持つことをお勧めします。
  2. InUseCounter を維持するために大きな単一のデータ構造を持つと、検索とデクリメントにより多くのロック時間が発生します
  3. 参照を見つけるための私のアプローチに代わる最良の方法は何でしょうか。また、ロック時間が非常に短い参照をすべての人が持っています。

アドバイスありがとうございます。

4

3 に答える 3

4
  1. 200 万のミューテックスは少し多すぎます。たとえそれらが軽量ロックであっても、いくらかのオーバーヘッドを占めます。
  2. を単一InUseCounterの構造体に配置すると、スレッドがレコードを解放するときにスレッド間の競合が発生します。スレッドがロックステップで実行されない場合、これは無視できる可能性があります。彼らが頻繁にレコードをリリースし、競合率が上がる場合、これは明らかにパフォーマンスの低下です。
  3. 1 つのスレッドがレコード参照カウントの維持を担当し (プロデューサー スレッド)、他のスレッドが別のキューを介してレコード リリース イベントを送り返すようにすることで、パフォーマンスを向上させることができます。つまり、プロデューサーをレコード リリース イベント コンシューマーに変えます。エントリをフラッシュする必要がある場合は、最初にすべてのリリース キューを処理してから、リリース ロジックを実行します。リリース イベントをすぐに処理しようとするのではなくキューに入れているため、対処するための遅延が発生しますが、パフォーマンスは大幅に向上するはずです。

ちなみに、これはDisruptorフレームワークの仕組みと似ています。これは、高頻度取引用の高性能 Java(!) 同時実行フレームワークです。はい、ハイパフォーマンス Java と並行性を同じ文で言いました。ハイ パフォーマンスの同時実行性の設計と実装に関する多くの貴重な洞察があります。

于 2012-01-10T08:40:18.813 に答える
1

すでにProducer->Consumerキューがあるので、非常に単純なシステムの1つは、「フィードバック」キュー(Consumer->Producer)を持つことです。

アイテムを消費した後、コンシューマーはポインターをプロデューサーにフィードバックして、プロデューサーがアイテムを削除し、キャッシュの「フリーリスト」を更新できるようにします。

このように、プロデューサーだけがキャッシュの内部に触れることがあり、そこで同期する必要はありません。キューだけを同期する必要があります。

于 2012-01-10T08:42:39.753 に答える
0
  1. はい、2000000 のミューテックスはやり過ぎです。
  2. 1 つの大きな構造物はより長くロックされますが、ロック/ロック解除の回数ははるかに少なくなります。
  3. 最善のアプローチは、shared_ptr スマート ポインターを使用することです。これらは、このために調整されているようです。自分でカウンターをチェックするのではなく、ポインターをクリーンアップするだけです。shared_ptr はスレッドセーフであり、それが指すデータではありませんが、1 つのプロデューサー (ライター) / N 人のコンシューマー (リーダー) の場合、これは問題になりません。
于 2012-01-10T08:32:08.297 に答える