2013-03-12 18 views
1

想知道是否可以按照以下方式遍歷STL容器(如向量)以避免讀/寫鎖定,但只允許任何「寫入」線程執行push_back()操作。使用索引安全遍歷STL容器以避免使用鎖?

for (size_t i = 0; i < vec.size(); i++) 
{ 
    const T& t = *vec[i]; 
    // do something with t 
} 

據我所知,迭代器可以通過更改容器或許,如果我們要確保初始容器尺寸爲任何未來增加足夠大的失效,它也應該是安全的遍歷元素,而無需鎖定讀取或寫道?

回答

3

想知道是否可以按照以下方式遍歷STL容器(如向量)以避免讀/寫鎖定,但只允許任何「寫入」線程執行push_back()操作。

不要,這不是線程安全的。考慮一個試圖讀取值並轉到當前緩衝區的線程。此時第二個線程正在增長緩衝區,在第一個線程獲得指向緩衝區的指針之後,但在它實際讀取值之前,第二個線程釋放緩衝區的內容。第一個線程正在讀取死對象,這是未定義的行爲。

將問題限制在一個reserve() -ed向量中(即避免增長緩衝區的問題),該方法仍然不是線程安全的。在push_back()的代碼會做類似的東西:

template <...> 
class vector { 
    T *begin,*end,*capacity; // common implementation uses 3 pointers 

    void push_back(T value) { 
     if (end == capacity) { /* grow vector and insert */ } 
     else { 
      new (end) T(value); 
      ++end; 
     } 
    } 
}; 

這裏的問題是,沒有同步機制的編譯器可以重新排列的指示,增加end並存儲到內存中,然後調用T構造在緩衝件。如果重排序發生,那麼您的讀者線程可能會看到值size(),其中包含當前正在存儲的值,但該值尚未存在於向量中。

+0

我想這個問題的最後一句話是試圖說容量足夠大,永遠不會讓它改變(儘管顯然沒有用正確的術語說)。 – GManNickG 2013-03-12 20:30:10

+0

@GManNickG準確地說。我不明白這個答案是否合格,如果容量足夠,它不應該增長?如果擔心某個特定的實現可能會修改緩衝區,那麼也許我應該只使用一個數組? – stgtscc 2013-03-12 21:40:40

+1

@stgtscc:當矢量緩衝區不需要增長時增加了另一個潛在問題。 – 2013-03-12 21:54:49

0

據我所知,你不應該編寫代碼來依賴它作爲實現細節,也不是矢量的導出API的一部分。如果它的記錄行爲,你可以依靠它,如果不是那麼做不這樣做。任何依賴於實現而不是API文檔部分的東西都可能在不同的平臺上以及在同一平臺上的不同版本的工具上發生變化。

另外,從@ GManNickG的評論 - >你將有一個競爭條件調用size(),因爲它將被修改和讀取而不鎖定在這裏。

+2

你仍然會在'size()'上出現競爭條件。 – GManNickG 2013-03-12 19:45:28

+0

你是對的,沒有想到通過。 – 2013-03-12 19:47:35

+0

競爭條件對size()有什麼可能的副作用?如果只能有一個作家呢? – stgtscc 2013-03-12 19:52:58

0

你不能依賴關於迭代器的建議不會失效(因爲剩下很多空間)。你需要shared_mutex和shared_lock。 (example of usage