2013-08-22 66 views
1

當我嘗試解決代碼中的併發問題時,遇到了此問題。在原始代碼中,我們只使用唯一鎖來鎖定寫入操作,該緩存是stl映射。但是對高速緩存的讀取操作沒有限制。所以我想爲讀操作添加一個共享鎖,並保持寫入的唯一鎖。但有人告訴我,由於一些內部緩存問題,在地圖上進行多線程並不安全。爲什麼地圖在C++中不是多線程安全的?

有人可以詳細解釋原因嗎?內部緩存是做什麼的?

+0

這似乎不是一個問題在這裏:http://stackoverflow.com/questions/8464458/c-pthread-how-to-make-map-access-threadsafe –

+0

**第二**答案此鏈接提供了一些有趣的算法信息:http://stackoverflow.com/questions/7078785/design-problem-thread-safety-of-stdmap –

+0

@KirkBackus:重新第一個鏈接:併發讀取是安全的,只要沒有寫作繼續。也就是說,讀者忽略的作者鎖會讓作者修改地圖,而讀者正在訪問地圖(反之亦然) –

回答

6

std::map的實現都必須滿足通常的 保證:如果您所有的做的是閱讀,那麼就沒有必要 外部synchrionization,但只要一個線程修改, 所有訪問必須同步。

我不清楚你的意思是「共享鎖定」;標準中沒有 這樣的東西。但是,如果任何一個線程正在寫入,您必須確保沒有其他線程可以同時讀取。 (如Posix的」 pthread_rwlock東西可以使用,但 沒有什麼標準相似,至少不是我能 找到了手。)

+0

在C++ 14中應該有一個'shared_mutex'(讀/寫互斥體)。與此同時,總是有[Boost](http://www.boost.org/doc/libs/release/doc/html/thread/synchronization.html#thread.synchronization.mutex_types.shared_mutex) –

+0

感謝您糾正我。 @MikeSeymour是正確的,它是來自boost的shared_mutex。可能我沒有清楚地說明我的問題。我不明白的不是如何使它線程安全,但爲什麼它不是多線程讀取安全。所以爲了簡化問題,考慮下面的場景,沒有寫操作,幾個線程同時讀取地圖是否安全? –

+0

@MikeSeymour如果它是共享的,它不是互斥體。它是一個讀寫鎖嗎? –

1

全部conststd類型的成員函數可以從C++ 11中的多個線程安全地調用,而無需顯式同步。事實上,任何與標準庫一起使用的類型(例如作爲容器的模板參數)必須滿足這一保證。說明:標準保證您的程序將具有所需的行爲,只要您永遠不會導致寫入和任何其他訪問同一數據位置而沒有同步點之間。這背後的理由是,現代CPU沒有嚴格的順序一致的內存模型,這會限制可伸縮性和性能。在引擎蓋下,你的編譯器和標準庫將在需要更強記憶順序的地方發出適當的內存限制。

+0

這是錯誤的。如果任何線程修改容器,則必須保護_all_訪問(包括通過'const'的訪問)。 (當然,這是正常的線程安全保證:標準庫中的所有容器都是線程安全的,因爲它們定義了一個合同,如果你符合那個合同,就沒有競爭條件。如果你) –

+0

當然,如果不同步調用非const成員函數,可能不會導致數據競爭,從而阻止其他同時訪問。這會導致未定義的行爲。 – mionic

+0

但是,如果任何線程正在使用非const函數,則還需要同步const訪問。 –

1

由於C++ 11至少對標準庫類的const操作保證是線程安全的(假設const存儲在其中的對象的操作是線程安全的)。

+3

而且「是線程安全的」:「只要* all *線程正在使用'const',你很好,如果有任何線程使用非''constst',你就不好,你必須同步。「 – Yakk

+0

所有操作都保證線程安全,因爲標準定義了與線程有關的合同,如果您符合該合同,則應該沒有問題。該合同表示,如果_any_線程修改了對象(容器),則所有線程中的_all_訪問必須受到保護。 –

+0

@Yakk我會說,如果任何線程正在使用非const,那麼您仍然很好,只要所有的訪問都以某種方式同步。 (這是標準定義線程安全的方式,它也是定義它的唯一明智的方法。) –

1

我真的不明白爲什麼會有任何緩存的問題。 ..

如果我參考map的stl定義,它應該被實現爲binary search tree

二叉搜索樹只是一個有鍵值節點池的樹。這些節點按照其鍵的自然順序排序,並且爲了避免任何問題,鍵必須是唯一的。所以根本不需要內部緩存。

由於不需要內部緩存,因此讀取操作在多線程環境中是安全的。但是對於寫操作而言,這不是同一個故事,對於那些必須提供自己的同步機制的人來說,對於任何非線程感知的數據結構。

請注意,當寫操作由線程執行時,您還必須禁止任何讀操作,因爲此寫操作可能導致二進制樹緩慢且完全地重新平衡,即在長時間內快速讀取操作寫操作會返回錯誤的結果。

+0

該標準不禁止緩存,並且實現可以想象地緩存最後訪問的元素,以加速對相同元素的多次訪問。 'std :: map'的接口被設計成很少需要多次訪問同一個元素,所以這可能不是一個好主意,但它是合法的。但是,如果實現這樣做,它仍然必須確保沒有競爭條件。 –

+0

我明白了,所以仍然可以使用短期內部緩存,但在多線程環境中不會產生影響。但是,當線程在地圖上執行寫操作時,最好禁止任何讀操作,因爲這可能導致整個二叉樹的完整且緩慢的重新平衡。 – OOEngineer

+0

基本上,這個標準沒有提到關於實現的任何內容,所以可以有任何可想象的緩存。所需要的是,如果沒有線程調用非const函數,則不需要外部同步;如果有任何內部狀態變化,那麼實現必須在內部安排同步,以便客戶端代碼不知道它(除了對性能的影響)。 –

相關問題