简体   繁体   English

同步对返回值的访问

[英]Synchronizing access to a return value

Consider the following C++ member function: 考虑以下C ++成员函数:

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

The intent here is not to synchronize access to the private member variable m_size , but just to make sure that the caller receives a valid value for m_size. 这里的目的不是同步访问私有成员变量m_size ,而只是为了确保调用者收到m_size的有效值。 The goal is to prevent the function from returning m_size at the same time that some other thread is modifying m_size . 我们的目标是防止函数返回m_size在一些其它线程正在修改的同时m_size

But is there any potential race-condition involved in calling this function? 但调用此功能是否涉及任何潜在的竞争条件? I'm not sure if the RAII style lock here is adequate to protect against a race condition. 我不确定这里的RAII风格锁是否足以抵御竞争条件。 Suppose the lock's destructor is called before the return value of the function is pushed onto the stack? 假设在将函数的返回值压入堆栈之前调用了锁的析构函数?

Would I need to do something like the following to guarantee thread-safety? 我是否需要执行以下操作以保证线程安全?

 size_t size() const
 {
    size_t ret;

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

    return ret;
 }

Both of your example constructs will do what you're looking for. 您的两个示例构造都将执行您正在寻找的内容。 The following information from the standard supports the behavior you're looking for (even in your 1st example): 标准中的以下信息支持您正在寻找的行为(即使在您的第一个示例中):

12.4/10 Destructors: 12.4 / 10析构函数:

Destructors are invoked implicitly ... for a constructed object with automatic storage duration (3.7.2) when the block in which the object is created exits. 对于具有自动存储持续时间(3.7.2)的构造对象,当创建对象的块退出时,将隐式调用析构函数。

And, 6.6/2 Jump statements (of which return is one): 并且,6.6 / 2跳转语句(其中return为1):

On exit from a scope (however accomplished), destructors (12.4) are called for all constructed objects with automatic storage duration (3.7.2) (named objects or temporaries) that are declared in that scope, in the reverse order of their declaration. 在从范围退出(但是已完成)时,将对所有具有自动存储持续时间(3.7.2)(命名对象或临时值)的构造对象调用析构函数(12.4),这些对象在其范围内声明,其顺序与其声明相反。 Transfer out of a loop, out of a block, or back past an initialized variable with automatic storage duration involves the destruction of variables with automatic storage duration that are in scope at the point transferred from but not at the point transferred to. 从循环中移出一个循环,或者从具有自动存储持续时间的初始化变量返回过去涉及销毁具有自动存储持续时间的变量,这些变量在转移点但不在转移点处的范围内。

So at the point of the return the lock variable is in scope and therefore the dtor has not been called. 因此,在returnlock变量在范围内,因此dtor尚未被调用。 Once the return has been executed, the dtor for the lock variable is called (thus releasing the lock). 一旦执行了return ,就会调用lock变量的dtor(从而释放锁定)。

Your first variant is safe, however you can't rely on this returned value to be consistent for any period of time. 您的第一个变体是安全的,但是您不能依赖此返回值在任何时间段内保持一致。 I mean for example don't use that returned value in a for loop to iterate over each element, because the real size could change right after the return. 我的意思是例如不要在for循环中使用返回的值来迭代每个元素,因为实际大小可能在返回后立即改变。

Basically you can think of it like this: a copy is needed of the return value, otherwise the destructor would be called hence possibly corrupting whatever the return value was before it was returned. 基本上你可以这样想:返回值需要一个副本,否则会调用析构函数,因此可能会在返回之前破坏返回值。

The destructor is called after the return statement. 在return语句之后调用析构函数。 Take this equivalent example: 拿这个等效的例子:

#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;
}

Your initial code is fine - the destructor will be called after the return value has been stored. 您的初始代码很好 - 在存储返回值之后将调用析构函数。 This is the very principle RAII operates on! 这是RAII运作的原则!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM