2013-08-01 110 views
0

我目前有一些多線程難題。我有兩個線程,一個讀取串行數據,另一個嘗試從數據中提取數據包。這兩個線程共享一個隊列。試圖創建包線程有一個名爲解析與以下聲明功能:指向STL容器線程安全的指針(隊列/ Deque)

Parse(std::queue<uint8_t>* data, pthread_mutex_t* lock); 

本質上講,它需要一個指向STL隊列,並使用pop()方法,因爲它經過隊列找包。由於任何pop()被鎖定,並且在Parse函數和將數據推送到隊列中的線程之間共享此鎖,所以使用該鎖。這樣,可以在數據被主動添加到隊列時解析隊列。

代碼似乎工作的大部分,但我看到無效數據包在一個比我想象的更高的速度。我的主要問題是,我想知道指針是否正在改變,而我正在讀取隊列中的數據。例如,如果第一個線程推送了一堆數據,那麼在內存中發現隊列的位置是否有可能改變?或者我保證即使添加數據,指向隊列的指針也會保持不變?我擔心隊列的內存可以在我的Parse()函數中重新分配,因此在我的函數中間,指針會失效。

例如,我知道某些STL迭代器對某些操作是無效的。但是,我傳遞一個指向容器本身的指針。就是這樣的:

// somewhere in my code I create a queue 
std::queue<uint8_t> queue; 
// elsewhere... 
Parse(&queue, &lock_shared_between_the_two_threads); 

指向容器本身的指針是否會失效?它指向什麼?第一個元素,或...?

請注意,我不是指向任何給定的元素,而是指向容器本身。另外,我從來沒有指定應該使用哪個底層容器來實現隊列,所以在它下面,它只是一個雙端隊列。

任何幫助將不勝感激。

編輯8/1:

我能夠在我的代碼上運行一些測試。幾點:

  1. 容器本身的指針不改變我的程序的生命週期。這是有道理的,因爲隊列本身是一個類的成員變量。也就是說,儘管隊列的元素是動態分配的,但隊列本身並不是這種情況。

  2. 我遇到的壞包似乎是我收到的串行數據的函數。我將所有數據都轉儲到了一個十六進制文件,並能夠找到無效的數據包,並且我的算法正確地將它們標記爲這樣。

其結果是,我在想傳遞一個引用或指針到一個STL容器放入一個函數是線程安全的,但我想聽到更多的評論確保這種情況下,或如果這是特定於實現的(因爲很多STL是...)。

+0

也許我錯過了它,但是你在* push()'和'pop()'操作中是否使用'lock'?如果不是,你應該是。 –

+0

對不起,是的,鎖用於所有推送和彈出操作。據我所知,這些操作應該沒有併發問題。 –

+0

您可以設計一個解析算法的單線程測試,以消除潛在的線程安全問題作爲潛在的原因嗎?基本上,捕獲大量的原始數據,構建一個隊列,然後讓解析器在它上面鬆動,並查看是否有同樣的問題。 –

回答

2

您擔心在一個線程中修改容器(添加/刪除節點)會以某種方式使指向另一個線程中容器的指針無效。容器是抽象的,並且除非您刪除容器對象本身,否則它將保持有效。由容器維護的數據的內存通常由stl::allocators分配到堆上。

根據容器對象本身的創建方式,這與爲堆棧,堆等分配容器對象本身的內存大不相同。 containerallocator之間的這種分隔是阻止對修改容器對象本身的數據進行一些修改。

爲了讓你的問題更簡單,就像Jonathan Reinhart所說的那樣,使它成爲一個單線程系統,讀取流並解析它。

在附註中,是否考慮過使用Boost Lookfree Queues或類似的東西。它們專爲這種類型的場景而設計。如果您正在接收數據包/頻繁讀取數據包,則鎖定隊列以便讀取/寫入每個數據包可能會成爲一項重大的性能開銷。

+0

「,除非你刪除了容器對象本身」,除非對象被*銷燬*(正如你正確地提到它們可以「在堆棧上」,以及它們不被*刪除*) – dyp

+0

是。 '刪除'堆棧對象肯定是[不推薦](http://stackoverflow.com/questions/441831/c-calling-delete-on-variable-allocated-on-the-stack):)。 –