2013-03-25 86 views
1

我有一个相关的问题,但不是,这个问题回答共享互斥与回报:C++参考

Example for boost shared_mutex (multiple reads/one write)?

我明白了排它锁是如何工作的,当操作中的作用域成员函数。我的问题是,通过引用返回函数呢?考虑以下(伪代码):

class A { 
    shared_mutex _mutex; 
    std::string _name; 

public: 
    const std::string& name() const {shared_lock(_mutex); return _name;} 
} 

然后假设我做这样的事情代码:

A obj; 
if (obj.name().length() >0) { ... }; 

我的直觉告诉我,这可能不是线程安全的,因为互斥体已经具有在length()函数被调用的时候超出了范围,但我不知道。

我想我也在问大图片,如果我试图让对象线程安全,应该避免通过引用完全返回?那岂不是让别人做这样的事情:

A obj; 
std::string& s = obj.name(); 
(at this point lock is out of scope) 
s = "foo"; // does this change the original object's member since it is a reference? 
+1

由于您提到的怀疑,它不是**线程安全的。返回副本而不是参考将是一个可行的解决方案。 – 2013-03-25 20:26:38

+0

@Drew Dormann,是否简单地从返回值中删除引用运算符强制它返回一个副本,还是编译器仍然可以优化它作为引用? – amnesia 2013-03-25 20:36:40

回答

2

它不是线程安全为name()返回之后的mutex将被释放,而另一个线程可以在通话期间或之前开始的_name修改std::string::length()或之后并且在输入的if的真正分支之前_name的状态将会改变。

要使对象线程安全,请确保在锁定mutex时发生对_name的所有访问。这意味着确保对_name的引用不会返回给调用者,也不会传递给提供给(未在原始代码中发布的虚构成员函数)成员函数A的回调,因为此回调可以将_name的地址缓存为未来,未同步,使用。

相反,返回或通过,由值(和创造_name副本的时候互斥锁定):

// Must be mutable to be modifiable 
// in a const member function. 
mutable shared_mutex _mutex; 

std::string name() const 
{ 
    // Must not create a temporary shared_lock, 
    // otherwise the lock will be released immediately. 
    // So give the shared_lock a name. 
    shared_lock lk(_mutex); 
    return _name; 
} 
+0

因此,为了确保按值返回,从返回中删除引用操作符就足够了吗?还是需要在函数中创建本地副本并将其返回? – amnesia 2013-03-25 20:41:43

+0

@amnesia,只是更新的答案。 – hmjd 2013-03-25 20:43:10

+0

非常好,谢谢! – amnesia 2013-03-25 20:43:59

1

只要改变这个功能delaration:

const std::string& name() const ... 

了这一点。

std::string name() const ... 

然后,您将返回字符串的副本。 (因为它是副本,所以不需要是const

该代码将是线程安全的,并且任何编译器优化都会保留该安全性。

0

由于性能方面的原因,大概您使用的是shared_mutex(而不是普通的互斥体),但复制字符串将会很昂贵。另外大概if (obj.name().length() >0) { ... }的大括号内的代码将进一步操纵字符串。如果是这种情况,那么您需要获取整个操作的互斥量,否则会遇到竞态条件。 (例如,您复制name,缩短其长度,然后与此同时其他人缩短name使其长度为0.大括号中的代码现在将对name的长度做出不正确的假设。)

如果可能的话,您可以考虑将要处理该字符串的代码作为class A的成员函数。

class A { 
    shared_mutex _mutex; 
    std::string _name; 

public: 
    rettype my_operation() { 
    shared_lock lk(_mutex); 
    if (_name.length() >0) { ... }; 
    } 
}; 

还有其他选择,但它们都涉及持有共享锁的整个期间,你取决于你不变的持有。