web-dev-qa-db-ja.com

constはC ++ 11でスレッドセーフを意味しますか?

constC++ 11thread-safeを意味すると聞きます。本当?

これは、constJavasynchronizedと同等になったことを意味しますか?

keywordsが不足していますか?

115
K-ballo

constC++ 11スレッドセーフを意味すると聞きます。本当?

それはsomewhattrue ...

これは、スレッドセーフについて標準言語が言わなければならないことです。

[1.10/4]2つの式の評価conflictいずれかがメモリ位置を変更する場合(1.7)もう1つは、同じメモリ位置にアクセスまたは変更します。

[1.10/21]2つの競合するアクションが含まれている場合、プログラムの実行にはdata raceが含まれます少なくとも1つのスレッドはアトミックではなく、他のスレッドよりも前に発生することはありません。そのようなデータの競合は、未定義の動作をもたらします。

data raceが発生するのに十分な条件に過ぎません:

  1. 特定のモノに対して同時に実行されるアクションは2つ以上あります。そして
  2. それらの少なくとも1つは書き込みです。

標準ライブラリはそれに基づいて構築されており、少し先に進んでいます:

[17.6.5.9/1]このセクションでは、データ競合を防ぐために実装が満たすべき要件を指定します(1.10)。特に指定のない限り、すべての標準ライブラリ関数は各要件を満たします。実装は、以下に指定された場合以外のデータの競合を防ぐことができます。

[17.6.5.9/3]C++標準ライブラリ関数は、他のスレッドからアクセス可能なオブジェクト(1.10)を直接または間接的に変更してはならないオブジェクトが関数の非const引数(thisを含む)を介して直接または間接的にアクセスされない限り、現在のスレッドよりも.

簡単な言葉で言うと、constオブジェクトに対する操作はthread-safeであると想定されています。つまり、独自のタイプのconstオブジェクトに対する操作でも、Standard Libraryはデータ競合を引き起こしません。

  1. 読み取りのみで構成されます。つまり、書き込みはありません。または
  2. 書き込みを内部的に同期します。

この期待が型の1つに当てはまらない場合、標準ライブラリのコンポーネントと一緒に直接または間接的に使用すると、データ競合になる可能性があります。結論として、const標準ライブラリの観点からスレッドセーフを意味します。これは単にcontractであり、コンパイラーによって強制されないことに注意することが重要です。これを破るとndefined behaviorが得られ、自分で実行します。 constが存在するかどうかは、少なくともdata races--に関してはコード生成に影響しません。

つまり、constJavasynchronizedと同等になりましたか?

いいえ。どういたしまして...

長方形を表す次の非常に単純化されたクラスを検討してください。

class rect {
    int width = 0, height = 0;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        width = new_width;
        height = new_height;
    }
    int area() const {
        return width * height;
    }
};

メンバー関数areaスレッドセーフ;です。 constが原因ではなく、読み取り操作のみで構成されているためです。関係する書き込みはなく、data raceが発生するために少なくとも1つの書き込みが必要です。つまり、必要な数のスレッドからareaを呼び出すことができ、常に正しい結果が得られます。

これは、rectthread-safeであることを意味しないことに注意してください。実際、特定のareaに対するset_sizeの呼び出しと同時にrectの呼び出しが発生した場合、areaが古い幅と新しい高さ(またはさらには値が文字化けします)。

しかし、それは大丈夫です、rectconstではないので、結局thread-safeになることさえ期待されていません。一方、const rectとして宣言されたオブジェクトは、書き込みができないためスレッドセーフになります(元々constで宣言されたものをconst_cast- ingすることを検討している場合、ndefined-behaviorを取得します_ 以上です)。

それでは、それはどういう意味ですか?

-議論のために-乗算演算は非常にコストがかかると仮定しましょう。可能な限りそれらを避けるべきです。要求された場合にのみ領域を計算し、将来再び要求された場合に備えてキャッシュすることができます。

class rect {
    int width = 0, height = 0;

    mutable int cached_area = 0;
    mutable bool cached_area_valid = true;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        cached_area_valid = ( width == new_width && height == new_height );
        width = new_width;
        height = new_height;
    }
    int area() const {
        if( !cached_area_valid ) {
            cached_area = width;
            cached_area *= height;
            cached_area_valid = true;
        }
        return cached_area;
    }
};

[この例が人工的すぎると思われる場合は、intを、本質的にスレッドセーフでなく乗算が非常に高価な非常に大きな動的に割り当てられた整数に精神的に置き換えることができます。

メンバー関数areaスレッドセーフではなくなりました。書き込みを行っており、内部的に同期されていません。それって問題ですか? areaへの呼び出しは、別のオブジェクトのcopy-constructorの一部として発生する場合があります。たとえば、constructor標準コンテナの操作によって呼び出される可能性があります。その時点で、標準ライブラリは、この操作がデータ競合に関してreadとして動作することを期待しています。しかし、私たちは書き込みを行っています!

rect標準コンテナに直接または間接的に配置すると、すぐに標準ライブラリとともに契約を入力します。 const関数で書き込みを続けながら、その契約を尊重するには、これらの書き込みを内部で同期する必要があります。

class rect {
    int width = 0, height = 0;

    mutable std::mutex cache_mutex;
    mutable int cached_area = 0;
    mutable bool cached_area_valid = true;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        if( new_width != width || new_height != height )
        {
            std::lock_guard< std::mutex > guard( cache_mutex );

            cached_area_valid = false;
        }
        width = new_width;
        height = new_height;
    }
    int area() const {
        std::lock_guard< std::mutex > guard( cache_mutex );

        if( !cached_area_valid ) {
            cached_area = width;
            cached_area *= height;
            cached_area_valid = true;
        }
        return cached_area;
    }
};

area関数thread-safeを作成しましたが、rectはまだthread-safeではありません。 areawidthへの割り当てがミューテックスによって保護されていないため、set_sizeへの呼び出しが間違った値を計算することになり、同時にheightへの呼び出しが発生する可能性があります。

本当にスレッドセーフrectが必要な場合は、同期プリミティブを使用してスレッドセーフではないrectを保護します。

keywordが不足していますか?

はい、そうです。初日からキーワードが不足しています。


ソースconstおよびmutableがわからない -He​​rb Sutter

129
K-ballo