2

私は、独自のスレッドでクライアントにサービスを提供し、他のタスクを実行するさまざまなワーカー スレッドを持つ、マルチスレッドの C++ サーバー アプリケーションを設計中です。

これらのスレッドはすべて、プロセスを終了して再起動することなく再ハッシュできる必要がある単一のテキスト構成ファイルに依存します。

私は現在、各クライアントとワーカースレッドに構成オブジェクトの独自のコピーを持たせ、再ハッシュごとにこの更新を行うことを検討しています。

これでわかったことの 1 つは、私が好み、上記のクラスの一部であるべきではないと思われる他のすべてのユーティリティ関数に構成を渡すことは、信じられないほど退屈に思える可能性があることです。

グローバル設定を使用すると、多くの同期が必要になることを除けば、はるかに簡単になります。

スレッド アプリケーションで一定でないグローバル構成に対処する方法についてのアイデアをお待ちしております。

4

5 に答える 5

5

私がこれを行うのが好きな方法は、構成を常に shared_ptr として const オブジェクトに運ぶことです。構成が必要なときはいつでも (一貫性を念頭に置いてください。要求処理コードの終わりまでに構成が技術的に古くなっている場合でも、同じ構成で要求全体を処理する方がよい場合がよくあります)、それに shared_ptr を取得します。バックグラウンド ワーカー タスクは、ワーク サイクルの都合のよい時点で shared_ptr の値をリセットし、構成を長時間保持しないようにします (古くなる可能性があるため)。

構成を変更できるタスクが 1 つしかない場合、これはうまく機能します。完全に新しい構成オブジェクトを構築し、その shared_ptr をリセットします。[編集]ロックで保護されています。以下を参照してください[/編集]。他のタスクが古い構成オブジェクトを使用しなくなるとすぐに、古い構成オブジェクトは消えます。

1 つの詳細: ポインターが続く限り、その構成オブジェクトへの shared_ptr を保持することが確実でない限り、構成オブジェクトの一部へのポインターを渡すことはできません。たとえば、サブ構成への名前のマップが構成に含まれている場合、これはイライラする可能性があります。

共有ポインターにはいくらかのオーバーヘッドがありますが、おそらく、構成のコピー全体を同期して維持するよりも少ないでしょう (もちろん、構成が小さい場合を除きます。もしそうなら、おそらくこの会話をしていないでしょう)。ほとんどのアプリでは構成の変更は比較的まれであるため、常に 3 つ以上の構成オブジェクトを持つことはほとんどありません。通常は、shared_ptr の作成をリクエストごとに 1 つにするように調整できるため、shared_ptr の同期は簡単です。

YMMVですが、かなりうまくいくことがわかりました。

[編集] 何人かのコメント投稿者が指摘したように、ロックの要件について明確にするべきでした。構成アップデーターは、読み取り/書き込みロックで保護されたマスター shared_ptr を保持します。ポインターを更新している間、書き込みロックを保持する必要があります。また、現在の構成に shared_ptr を返すインターフェイスをエクスポートします。そのインターフェイスは、読み取りロックを保持しながら shared_ptr をコピーします。構成の変更はめったになく、shared_ptr は非常に小さいため、ロックの競合はほとんどありません。[1]

構成タスク自体は別として、他の shared_ptr は複数のタスクで共有されるべきではないため、ロックについて心配する必要はありません。すべてのタスクは独自のものを取得する必要があります。

[1] これを書いているときに、マスター shared_ptr で .reset を呼び出すというこれまでのやり方では、構成のデストラクタが遅い場合 (たとえば、構成には膨大な数の std::string が含まれています)。reset の実装 (一時的な NULL shared_ptr との単なるスワップ) を拡張して、スワップをロック ガード内に配置し、(一時的な) デストラクタをロック解除して実行する方がよい場合があります。ただし、構成の変更が(少なくとも私が関連付けられているサーバーでは)めったにないことを考えると、それがかなりの違いをもたらすかどうかは疑問です.

于 2012-09-19T05:49:37.947 に答える
1

これは、すべてのスレッドで共有される 1 つの構成オブジェクトで処理できます。この構成クラスには 2 つの部分が必要です。

  1. 構成ファイルの読み取りと解析
  2. すべてのスレッドによる構成データへのアクセス

構成ファイルの読み取りと解析は、できればメイン スレッドから 1 つのスレッドだけで処理する必要があります。この部分は、残りのスレッドから分離されます。更新が行われると、残りのスレッドがアクセスする構成データは、書き込みロックを使用して pthread_rwlock を介して更新できます。スレッドが構成データにアクセスするときは、読み取りロックを使用する必要があります。

rw(読み取り/書き込み)ロックに慣れていない場合は、書き込みロックが実行されていない限り、ブロックされない複数の同時読み取りロックが存在する可能性があります。一度に設定できる書き込みロックは 1 つだけです。したがって、このコンテキストでは、すべてのスレッドがロック競合なしで構成データを同時に読み取ることができます。再ハッシュ後にメインスレッドによって構成データが更新されたときにのみ、読み取り中にロックが発生します。

于 2012-09-19T05:38:18.920 に答える
1

この部分がわかりません:

これでわかったことの 1 つは、私が好み、上記のクラスの一部であるべきではないと思う他のすべてのユーティリティ関数に構成を渡すことは、信じられないほど面倒に思えることです。

とにかく、thread-local storageを調べてください。スレッドローカルなシングルトン構成オブジェクトを作成できます。

于 2012-09-19T05:03:31.283 に答える
0

すでに投稿されているものに加えて、他の良い解決策は DB テーブルです。DB は、構成を使用してトランザクションをより適切に制御できるため、データの整合性が向上します。変更が古い古い構成に基づいているため、既存の構成に不適切な変更が加えられた場合からの保護を提供できます。

はい、OPの使用には遅く、おそらくやり過ぎです(特にDBがまだない場合)が、完全を期すために言及する必要があります。

于 2012-09-19T09:58:37.190 に答える
0

定義上、マルチスレッド アプリケーションで変更可能なグローバル オブジェクトを使用するには、スレッドがオブジェクトにアクセスして変更する場合に同期が必要になります。いくつかの注意点がありますが、グローバル構成を保持できますが、構成にアクセスするときにレイヤーを追加しても、同期について心配する必要はありません。すべてのスレッドはグローバル構成へのポインターを持つことができ、すべてのスレッドが単に構成から読み取っている場合、これは問題ありません。ただし、構成を変更する必要がある場合は、アクセスしているメンバーにフラグを設定し、それをスレッドのローカルに保存します。構成にアクセスするたびに、このフラグをチェックしてから、適切なデータを返す必要があります。これは、コードの設計に非常に大きな利点があるだけでなく、構成へのインターフェースを具体化する必要があります。

于 2012-09-19T05:08:12.053 に答える