std::recursive_mutex
デフォルトでありstd::mutex
、パフォーマンスの最適化と見なす必要がありますか?
そうではありません。非再帰ロックを使用する利点は、パフォーマンスの最適化だけではありません。コードが、リーフレベルのアトミック操作が実際にはリーフレベルであることを自己チェックしていることを意味し、ロックを使用する他の何かを呼び出していません。
あなたが持っているかなり一般的な状況があります:
- シリアル化が必要な操作を実装する関数で、mutex を使用して実行します。
- より大きなシリアル化された操作を実装し、最初の関数を呼び出してその 1 つのステップを実行しようとしている別の関数が、より大きな操作のロックを保持している場合。
具体的な例として、おそらく最初の関数はリストからノードをアトミックに削除し、2 番目の関数はリストから 2 つのノードをアトミックに削除します (2 つのノードのうち 1 つだけが取得されたリストを別のスレッドに表示させたくありません)。アウト)。
これには再帰的ミューテックスは必要ありません。たとえば、最初の関数をパブリック関数としてリファクタリングして、ロックを取得し、「安全でない」操作を行うプライベート関数を呼び出すことができます。2 番目の関数は、同じプライベート関数を呼び出すことができます。
ただし、代わりに再帰的ミューテックスを使用すると便利な場合があります。この設計にはまだ問題があります:クラスの不変条件が保持されないポイントでのremove_two_nodes
呼び出しremove_one_node
(2 回目の呼び出しでは、リストは正確に公開したくない状態になります)。remove_one_node
しかし、 がその不変条件に依存していないことがわかっていると仮定すると、これは設計上の重大な欠陥ではなく、理想よりも少し複雑なルールを作成しただけです。入った」。
したがって、このトリックは時々役に立ちます。私はこの記事ほど、再帰的ミューテックスを嫌いではありません。私は、Posix にそれらを含める理由が、「mutex 属性とスレッド拡張機能を示すため」という記事の内容とは異なると主張する歴史的知識を持っていません。ただし、私は確かにそれらをデフォルトとは考えていません。
設計で再帰ロックが必要かどうかわからない場合は、設計が不完全であると言っても過言ではありません。コードを書いていて、ロックがすでに保持されているかどうかなど、基本的に重要なことを知らないという事実を後で後悔するでしょう。したがって、「万が一に備えて」再帰ロックを入れないでください。
必要であることがわかっている場合は、それを使用してください。必要がないことがわかっている場合、非再帰ロックを使用することは単なる最適化ではなく、設計の制約を強化するのに役立ちます。2 番目のロックが失敗するほうが、ロックが成功して、設計上絶対に発生してはならないことを誤って実行してしまったという事実を隠すよりも便利です。しかし、自分の設計に従い、ミューテックスを二重にロックしないと、それが再帰的であるかどうかを知ることができず、再帰的ミューテックスは直接害を及ぼすことはありません。
この類推は失敗するかもしれませんが、別の見方をすると次のようになります。2 種類のポインターから選択したとします。1 つはヌル ポインターを逆参照するときにスタック トレースでプログラムを中止し、もう 1 つは戻ります0
(またはそれをより多くの型に拡張する: ポインターが値を参照しているかのように動作します)。初期化されたオブジェクト)。非再帰的ミューテックスはアボートするものに少し似ており、再帰的ミューテックスは 0 を返すものに少し似ています。どちらにも用途がある可能性があります。 -value"値。ただし、ヌル ポインターを逆参照しないようにコードが設計されている場合は、それを黙って許可するバージョンを既定で使用したくありません。