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 跳转语句(其中return之一):

在从范围退出时(无论如何完成),对于在该范围中声明的所有具有自动存储持续时间 (3.7.2) 的构造对象(命名对象或临时对象),按照其声明的相反顺序调用析构函数 (12.4)。转移出循环、转移出块或返回具有自动存储持续时间的已初始化变量涉及销毁具有自动存储持续时间的变量,这些变量在转移点的范围内但不在转移点的范围内。

因此,return变量lock在范围内,因此未调用 dtor。一旦return执行完毕,就会调用变量的 dtor lock(从而释放锁)。

于 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 回答