2010-10-27 131 views
18

我正在使用隊列在線程之間進行通信。我有一個閱讀器和多個寫入器線程。我的問題是,當我使用閱讀器隊列中的push/front/pop時,是否需要鎖定隊列?我可以這樣做以下:STL隊列的線程安全

//reader threads 
getLock(); 
get the number of elements from the queue 
releaseLock(); 

int i = 0; 
while(i < numOfElements){ 
    queue.front(); 
    queue.pop(); 
    i++ 
} 

的想法是,我想減少鎖定代碼和自寫線程只會寫入隊列後面的粒度,並且只有一個單一的閱讀器線。只要我得到元素的數量,那麼我可以從隊列中獲取元素,或者我是否需要將front()pop()也包含在鎖中?

回答

8

任何類型的沒有明確說明它的線程安全保證應始終由一個互斥體來控制。也就是說,你的實現的stdlib可能會允許一些變化 - 但你不能知道所有std :: queue的實現。

由於性病::隊列包裝另一個容器(這是一個容器適配器),你需要看看下面的容器,默認爲雙端隊列。

您可能會發現更容易,更好,更便攜編寫自己的容器適配器,讓你需要的保證。我不知道這些事情是否適用於Boost中的隊列。

我沒有看過的C++ 0x足以知道其是否具有該出的現成的任何解決方案,但可能是另一種選擇。

+4

的C++ 0x具有原子公司提供的情況和標準執行線程安全的,在C無鎖隊列,所以它使程序員能夠無鎖的一個很好的解釋(線程安全)算法,並提供標準互斥鎖,但它沒有任何開箱即用的功能。 – GManNickG 2010-10-27 02:32:27

2

這完全是依賴於實現的。 C++標準提到了線程或線程安全性,因此這是否會起作用取決於您的實現如何處理隊列元素。

在你的情況,讀者實際上是彈出隊列,這被認爲是一個寫操作。我懷疑在這種情況下,當多個線程同時寫入一個容器時,任何常見的實現實際上都能保證線程安全。至少VC++沒有:

對於讀取到同一個對象,當其他線程上沒有寫入者時,該對象是線程安全的以供讀取。

對於寫入同一個對象,該對象對於在其他線程上沒有讀取器時從一個線程寫入的線程安全。

9

正如其他人已經提到的那樣,標準容器不需要保證線程安全,所以你要求的東西不能被移植實現。通過使用2個隊列和一個隊列指針,可以減少讀者線程鎖定寫入者的時間,該隊列指針指示寫入者當前正在使用的隊列。

每個寫入器將:

  • 採集鎖
  • 推元件(一個或多個)到隊列中當前由隊列指針指向
  • 推出鎖

讀者然後可以做如下:

  • Acquir e鎖定
  • 切換隊列指針從所述第一隊列指向所述第二隊列
  • 推出鎖
  • 過程元件
+0

作者將如何工作? – GManNickG 2010-10-27 02:27:22

+2

作者總是追加到隊列指針當前指向的任何隊列(在第一次獲取鎖之後)。在這種情況下,鎖正在保護指針當前引用的任何隊列(和指針本身);另一個隊列(「第一隊列」)可以由沒有鎖的讀卡器處理。 – 2010-10-27 02:47:21

1

有時,可以通過避免之間共享狀態或資源解決了很多併發頭痛的線程。如果你有多個線程同時訪問一個容器以推進他們的工作,然後嘗試讓他們在專用容器上工作。在特定的時間點,您將以不同時的方式將容器的元素收集到中央容器上。

如果您可以避免在線程間共享狀態或資源,那麼同時運行線程並沒有問題。線程不需要擔心對方,因爲它們是完全隔離的,彼此不起任何作用。

1

你的預感是正確的:即使你不能指望STD隊列是線程安全的,隊列應該是線程安全的設計

的這是爲什麼++由van Dooren

+2

對於特殊情況下的單一生產者,單個消費者以及關於編譯器生成的代碼中的內存屏障和原子讀/寫的一些更多假設,天真的鎖免費隊列實現僅部分「線程安全」。然而,實現真正的鎖定空閒線程安全隊列是可能的,但並不那麼簡單。 – Leo 2015-07-25 01:00:08