考虑以下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;
}
答案 0 :(得分:12)
您的两个示例构造都将执行您正在寻找的内容。标准中的以下信息支持您正在寻找的行为(即使在第一个示例中):
12.4 / 10 Destructors:
对于具有自动存储持续时间(3.7.2)的构造对象,当创建对象的块退出时,将隐式调用析构函数。
并且,6.6 / 2跳转语句(其中return
是一个):
从范围退出(无论多么完成)时,将为所有具有自动存储持续时间(3.7.2)(命名对象或临时值)的构造对象调用析构函数(12.4),这些对象在该范围内声明,顺序与他们的声明。从一个循环中转出一个循环,或者通过一个具有自动存储持续时间的初始化变量返回过去涉及销毁具有自动存储持续时间的变量,这些变量在转移点但不在转移点的范围内。
因此return
lock
变量在范围内,因此没有调用dtor。执行return
后,将调用lock
变量的dtor(从而释放锁定)。
答案 1 :(得分: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;
}
答案 2 :(得分:1)
您的初始代码很好 - 在存储返回值之后将调用析构函数。这是RAII运作的原则!