私がこれを行うのが好きな方法は、構成を常に 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 との単なるスワップ) を拡張して、スワップをロック ガード内に配置し、(一時的な) デストラクタをロック解除して実行する方がよい場合があります。ただし、構成の変更が(少なくとも私が関連付けられているサーバーでは)めったにないことを考えると、それがかなりの違いをもたらすかどうかは疑問です.