2013-03-25 59 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) { ... }; 
    } 
}; 

還有其他選擇,但它們都涉及持有共享鎖的整個期間,你取決於你不變的持有。