14

次の C++ メンバー関数を検討してください。

 size_t size() const
 {
    boost::lock_guard<boost::mutex> lock(m_mutex);
    return m_size;
 }

ここでの目的は、プライベート メンバー変数へのアクセスを同期することではなくm_size、呼び出し元が m_size の有効な値を確実に受け取るようにすることです。m_size目標は、他のスレッドが変更しているときに関数が同時に返されないようにすることm_sizeです。

しかし、この関数の呼び出しに潜在的な競合状態はありますか? ここでの RAII スタイルのロックが競合状態から保護するのに十分かどうかはわかりません。関数の戻り値がスタックにプッシュされる前に、ロックのデストラクタが呼び出されたとします。

スレッドセーフを保証するために、次のようなことをする必要がありますか?

 size_t size() const
 {
    size_t ret;

    {
      boost::lock_guard<boost::mutex> lock(m_mutex);
      ret = m_size;
    }

    return ret;
 }
4

3 に答える 3

12

あなたの例の構成は両方とも、あなたが探していることを行います。標準からの次の情報は、探している動作をサポートしています(最初の例でも):

12.4/10 デストラクタ:

デストラクタは暗黙的に呼び出されます...オブジェクトが作成されたブロックが終了すると、自動ストレージ期間(3.7.2)で構築されたオブジェクトに対して。

そして、6.6/2 Jump ステートメント (そのうちのreturn1 つ):

スコープから出ると (どのように達成されても)、デストラクタ (12.4) は、そのスコープで宣言された自動保存期間 (3.7.2) (名前付きオブジェクトまたは一時オブジェクト) を持つすべての構築済みオブジェクトに対して、宣言の逆の順序で呼び出されます。ループからの転送、ブロックからの転送、または自動ストレージ期間を持つ初期化された変数の過去への転送には、転送元のポイントではスコープ内にあるが転送先のポイントではスコープ内にない、自動ストレージ期間を持つ変数の破棄が含まれます。

returnそのため、変数の時点でlockスコープ内にあるため、dtor は呼び出されていません。がreturn実行されると、変数の dtorlockが呼び出されます (したがって、ロックが解除されます)。

于 2010-07-08T01:35:38.377 に答える
3

最初のバリアントは安全ですが、この戻り値が一定期間一貫しているとは限りません。たとえば、 for ループでその戻り値を使用して各要素を反復処理しないでください。実際のサイズは、戻りの直後に変化する可能性があるためです。

基本的には、次のように考えることができます。戻り値のコピーが必要です。そうしないと、デストラクタが呼び出されて、返される前の戻り値が何であれ破損する可能性があります。

デストラクタは、return ステートメントの後に呼び出されます。次の同等の例を見てください。

#include <assert.h>

class A
{
public:
    ~A()
    {
        x = 10;
    }
    int x;
};

int test()
{
    A a;
    a.x = 5;
    return a.x;
}

int main(int argc, char* argv[])
{
    int x = test();
    assert(x == 5);
    return 0;
}
于 2010-07-08T01:15:45.633 に答える
1

初期コードは問題ありません。戻り値が格納された後にデストラクタが呼び出されます。これがRAIIの動作原理です。

于 2010-07-08T01:09:24.617 に答える