私はこのビデオに入れられました:
http://channel9.msdn.com/posts/C-and-Beyond-2012-Herb-Sutter-You-dont-know-blank-and-blank
https://codereview.stackexchange.com/users/39810/glampert
私がこの質問をしたので:
https://codereview.stackexchange.com/questions/82616/const-cast-of-stdmutex-in-get-member
クラスに_std::mutex
_をmutable
としてマークすることは、私の元の質問に対する正しい答えであり、ビデオは、その周りの優れた楽しいディスカッションです。ちらほらと感謝します。
ただし、「const
== thread-safe」(タイムポイント16:00を参照)という話の前提には同意しません。
以下は、マルチスレッドの世界でconst
をどのように解釈すべきかについての私の提案です。これはHerbの提案ほど単純ではありませんが、マルチスレッドの答えのいずれかが単純であると考える場合、マルチスレッドの課題を完全に理解できていません。
コメントお願いします。 const
はマルチスレッドプログラムにとってどのような意味があると思いますか?その意味は根本的に変化しましたか?
const
は、問題のconst
参照による逆参照によって論理的に変更されていないことを意味します。マルチスレッドの脚注#1:マルチスレッドプログラムの共有状態の一部を形成するオブジェクトでは、「論理的に未変更」は「論理的に
const
メンバー」を意味します関数はオブジェクトの読み取りのみを実行する」ため、実際に書き込みを実行する実装(キャッシュされた値、インスツルメンテーション、さらには同期ミューテックスなど)を適切にスレッドセーフにして、「読み取りのみ」の「論理的な」外観を維持する必要があります。シングルスレッドの脚注:もちろん、シングルスレッドのアプリケーションでも、
const
は、オブジェクトの存続期間中にオブジェクトが変更されないことを保証するものではありません。const
の参照が指定されています。const
以外の参照(エイリアス)が存在する可能性があり、単一のスレッド化プログラムでも、1つ以上のconst
参照を持つオブジェクトを変更するために使用される場合があります。マルチスレッドの脚注#2:
const
への参照があるためにオブジェクトが変更されないという保証の欠如は、マルチスレッドアプリケーション。つまり、多くの意味で、変化はありません。
私の最大の懸念は、Herb Sutterが恐ろしい誤りであると確信していることを確信しているため、const
を「スレッドセーフ」として読み取ると、次のように簡単につながることを恐れています。
std::lock_guard guard(this->mutex)
を追加するという誤りは、なんらかの方法でそれを「スレッドセーフ」に使用することになります。const
メソッドのみを呼び出す場合、アプリケーションにはスレッドセーフの保証があり、そうでない場合は実装者に不満を言う可能性があるという誤りです。mutable
メンバーを変更せず、const
をどこにもキャストしない場合、(見かけ上)「const
はスレッドセーフ」を意味するため、スレッドセーフについて心配する必要はありません。Fallacy 1は、メソッドがロックガードされていることがわかっているという理由だけで、別のスレッドによって変更されているコレクションを反復処理することができると想定することによって最もよく例示されます。
Fallacy 2は、すべてのメソッドに(もちろん)ロックガードを追加できるが、(せいぜい)時間を浪費しているだけで(最悪の場合)誤ってfalseに設定している実装者に、潜在的に面倒な(そして不可能でさえある)負担を押し付けています1 。
誤謬3は、スレッドセーフがすべて同時アクセスを競うことに関する誤謬です。ただし、メモリキャッシングは、const
メソッド内で一貫した状態を取得するために、何らかの種類のメモリバリアが実行されていることを確認する必要がある場合があることを意味します(少なくとも「取得」など)。
言語機能としてのconst
はスレッドの安全性を保証しませんが、定数関数の多くは、通常、いくつかの定数変数を読み取り、結果を計算して返すだけなので、その性質上、たまたまスレッドセーフです。これは、ビデオで言及されているビット単位の定数です。内部同期が必要な場合(私は信じます)は少数です。
これ自体は、関数が定数であるためスレッドセーフであると直感的に考えるプログラマーに(誤った)セキュリティ感覚を生み出すことがわかります。
したがって、私はこの規則を個人的にサポートしています。つまり、関数は定数であるがビット単位の定数ではない場合は、内部的に同期してスレッドセーフにします。
プレゼンテーションで述べたように、この規約はSTLでも採用されており、ユーザーの説得力をさらに強化します。
クラスでconstメソッドのみを呼び出す場合、アプリケーションにはスレッドセーフの保証があり、そうでない場合は実装者に不満を言う可能性があるという誤りです。
これは意図されていると思います。ライブラリのユーザーとして、それを慎重に扱っても害はありませんが、ライブラリの実装者として、この規則に従うことをお勧めします。
誤謬3は、スレッドセーフがすべて同時アクセスを競うことに関する誤謬です。ただし、メモリキャッシングとは、constメソッド内でコヒーレントな状態を取得するために、何らかのメモリバリアが実行されていることを確認する必要がある場合があることを意味します(少なくとも「取得」など)。
たぶん、const == thread-safeは、const ==> thread-safeと書く方がよいでしょう。 Herb Sutterの意図は、const
がスレッドの安全性における究極の特効薬であると述べることではなかったと思います。私の解釈では、メッセージはもうありません。つまり、const
関数を作成するときは、スレッドセーフにすることも意図しているはずです。
スレッドセーフ自体を意味するものではありませんが、標準ライブラリの場合は次のようになります。
17.6.5.9データ競合の回避[res.on.data.races]
1このセクションでは、データ競合を防ぐために実装が満たす必要のある要件を指定します(1.10)。特に指定のない限り、すべての標準ライブラリ関数は各要件を満たします。実装は、以下に指定されている以外の場合にデータの競合を防ぐ可能性があります。
2 C++標準ライブラリ関数は、this
を含む関数の引数を介して直接または間接的にアクセスされない限り、現在のスレッド以外のスレッドがアクセスできるオブジェクト(1.10)に直接または間接的にアクセスしてはなりません。
3 C++標準ライブラリ関数は、this
。
[...]
そして、それはユーザー定義型と関数が合理的に可能な限り従うべきパターンです。
たとえばC++では、constクラスまたは構造体に変更可能なメンバーを含めることができます。可変メンバーは通常、オブジェクトの論理値が変更されない状況で使用されますが、内部表現は変更されます。
可変メンバーを変更しても、自動的にスレッドセーフになるわけではありません。したがって、constオブジェクトへのアクセスは、自動的にスレッドセーフではありません。
別の例は、ファイルへのポインターを保持するクラスです。インスタンスがconstであっても、そのファイルから読み書きできます。しかし、これらの操作がスレッドセーフになる可能性はほとんどありません。