104

これは 2 つの部分からなるちょっとした質問で、すべて のアトミック性に関するものですstd::shared_ptr

1. 私が知る限り、それはアトミックstd::shared_ptrな唯一のスマートポインターです。<memory>の非アトミック バージョンがstd::shared_ptr利用可能かどうか疑問に思っています ( <memory>. boost::shared_ptrアトミックでもあることは知っていますが(BOOST_SP_DISABLE_THREADS定義されていない場合)、別の選択肢があるのでしょうか?と同じセマンティクスを持つものを探していますがstd::shared_ptr、原子性はありません。

2.std::shared_ptrアトミックである理由がわかりました。ちょっといいですね。ただし、すべての状況に適しているわけではなく、C++ には歴史的に「使用した分だけ支払う」というマントラがありました。複数のスレッドを使用していない場合、または複数のスレッドを使用しているがスレッド間でポインターの所有権を共有していない場合、アトミック スマート ポインターは過剰です。2 番目の質問は、なぜC++11で非アトミック バージョンが提供されなかったのかということstd::shared_ptrです。(理由があると仮定して) (答えが単純に「非アトミック バージョンはまったく考慮されなかった」または「誰も非アトミック バージョンを要求したことがない」である場合は問題ありません!)。

shared_ptr質問 2 で、誰かが の非アトミック バージョンを(Boost または標準化委員会に) ( のアトミック バージョンを置き換えるのではshared_ptrなく、それと共存させるために)提案したことがあるかどうか疑問に思っています。特定の理由。

4

5 に答える 5

117

1. 非アトミック バージョンの std::shared_ptr が利用可能かどうか疑問に思っています

標準では提供されていません。「サードパーティ」ライブラリによって提供されるものがあるかもしれません。実際、C++11 より前、および Boost より前は、誰もが独自の参照カウント スマート ポインターを作成したように見えました (私を含む)。

2. 私の 2 番目の質問は、C++11 で std::shared_ptr の非アトミック バージョンが提供されなかったのはなぜですか?

この問題は、2010 年の Rapperswil 会議で議論されました。この問題は、スイスの National Body Comment #20 で紹介されました。あなたが質問で提供したものを含め、議論の両側に強力な議論がありました. ただし、議論の最後に、非同期 (非アトミック) バージョンのshared_ptr.

含まれている反対の引数:

  • 同期されていない shared_ptr で記述されたコードは、最終的にスレッド化されたコードで使用され、警告なしでデバッグが困難な問題を引き起こす可能性があります。

  • 参照カウントでトラフィックする「一方向」である「ユニバーサル」shared_ptrを1つ持つことには利点があります。元の提案から:

    使用する機能に関係なく同じオブジェクト タイプを持ち、サードパーティ ライブラリを含むライブラリ間の相互運用性を大幅に促進します。

  • アトミックのコストは、ゼロではありませんが、圧倒的ではありません。コストは、アトミック操作を使用する必要のないムーブ コンストラクションとムーブ割り当てを使用することで軽減されます。このような操作は、通常、vector<shared_ptr<T>>消去と挿入で使用されます。

  • それが本当にやりたいことであれば、人々が独自の非アトミック参照カウント スマート ポインターを作成することを禁止するものは何もありません。

その日のラッパースウィルのLWGからの最後の言葉は次のとおりでした。

CH 20 を拒否します。現時点では、変更を行うコンセンサスはありません。

于 2013-02-28T16:10:02.003 に答える
56

Howard は既にこの質問に適切に答えており、Nicol は互換性のない多くのポインター型ではなく、単一の標準共有ポインター型を持つことの利点についていくつかの良い点を挙げています。

shared_ptr私は委員会の決定に完全に同意しますが、非同期のようなタイプを特別な場合に使用することにはいくつかの利点があると思います。そのため、このトピックについて数回調査しました。

複数のスレッドを使用していない場合、または複数のスレッドを使用しているがスレッド間でポインターの所有権を共有していない場合、アトミック スマート ポインターは過剰です。

プログラムが複数のスレッドを使用しない場合の GCC では、shared_ptr は refcount にアトミック ops を使用しません。これは、プログラムがマルチスレッドであるかどうかを検出するラッパー関数を介して参照カウントを更新することによって行われ (GNU/Linux では、プログラムがシングルスレッドであるかどうかを示す Glibc の特殊変数をチェックすることによって行われます[1] )、atomic にディスパッチします。またはそれに応じて非アトミック操作。

何年も前に、GCCは基底クラスshared_ptr<T>として実装されているため、明示的に を使用することで、マルチスレッド コードでもシングルスレッド ロック ポリシーで基底クラスを使用できることに気付きました。このようなエイリアス テンプレートを使用して、スレッド セーフではない共有ポインタ型を定義できますが、わずかに高速です[2] :__shared_ptr<T, _LockPolicy>__shared_ptr<T, __gnu_cxx::_S_single>

template<typename T>
  using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;

このタイプは相互運用性がなく、ユーザーが提供する追加の同期なしではオブジェクトがスレッド間で決して共有されないことstd::shared_ptr<T>が保証されている場合にのみ安全に使用できます。shared_ptr_unsynchronized

もちろん、これは完全に移植性がありませんが、場合によっては問題ありません。shared_ptr_unsynchronized<T>が のエイリアスである場合、適切なプリプロセッサのハックを使用すると、コードは他の実装でも問題なく動作shared_ptr<T>しますが、GCC では少し高速になります。


[1] Glibc 2.33 がその変数を追加する前は、ラッパー関数はlibpthread.so、シングルスレッドとマルチスレッドをチェックする不完全な方法として、プログラムが にリンクしているかどうかを検出していました。

[2] 残念ながら、それは意図された使用例ではなかったため、GCC 4.9 より前では最適に動作しませんでした。一部の操作はまだラッパー関数を使用していたため、`_S_single` ポリシーを明示的に要求した場合でもアトミック操作にディスパッチされました。 . 詳細については、http: //gcc.gnu.org/ml/libstdc++/2007-10/msg00180.htmlのポイント (2)と、マルチスレッド アプリでも非アトミック実装を使用できるようにする GCC へのパッチを参照してください。私は何年もそのパッチに座っていましたが、最終的に GCC 4.9 にコミットしました。
于 2013-02-28T17:26:55.400 に答える
4

職場で shared_ptr に関する講演を準備しています。私は、個別の malloc を回避する (make_shared ができることのように) 変更されたブースト shared_ptr と、上記の shared_ptr_unsynchronized のようなロック ポリシーのテンプレート パラメーターを使用しています。私はからのプログラムを使用しています

http://flyingfrogblog.blogspot.hk/2011/01/boosts-sharedptr-up-to-10-slower-than.html

テストとして、不要な shared_ptr コピーをクリーンアップした後。プログラムはメイン スレッドのみを使用し、テスト引数が表示されます。テスト環境は、linuxmint 14 を実行しているノートブックです。所要時間は秒単位です。

test run setup boost(1.49) std with make_share modified boost
mt-unsafe(11) 11.9 9/11.5(-pthread オン) 8.4  
アトミック(11) 13.6 12.4 13.0  
mt-unsafe(12) 113.5 85.8/108.9(-pthread オン) 81.5  
アトミック(12) 126.0 109.1 123.6  

「std」バージョンのみが -std=cxx11 を使用し、-pthread は g++ __shared_ptr クラスの lock_policy を切り替える可能性があります。

これらの数値から、アトミック命令がコードの最適化に与える影響がわかります。このテスト ケースでは C++ コンテナーは使用vector<shared_ptr<some_small_POD>>されていませんが、オブジェクトがスレッド保護を必要としない場合は問題が発生する可能性があります。追加の malloc がインライン化とコードの最適化の量を制限しているため、Boost の影響は少ないと思われます。

アトミック命令のスケーラビリティをテストするのに十分なコアを備えたマシンをまだ見つけていませんが、必要な場合にのみ std::shared_ptr を使用する方がよいでしょう。

于 2013-05-30T04:44:51.137 に答える