2011-12-29 22 views
5

我正在使用valgrind運行多線程套接字程序。客戶端將通過TCP向服務器發送請求,然後忙於等待布爾值。當調用服務於服務器響應的回調函數時,布爾值將被設置。一旦接收到響應(並設置了布爾標誌),服務器將再次發出請求,並在循環中重複執行此操作。我知道對共享變量(布爾值)的非同步訪問可能會導致線程問題,但我嘗試過使用pthread互斥體,並且程序減速了大約20%(這裏速度很重要)。我相信寫入共享布爾變量是好的,因爲它可以在一個週期內完成。valgrind在多線程套接字程序中失速

該程序在valgrind之外運行良好,但通常在使用valgrind運行時會停止運行。我離開程序在一夜之間運行..通常需要幾秒鐘才能完成,所以我不認爲這是一個等待程序完成的時間不夠的情況。線程由開源引擎框架(快速修復)管理,所以我認爲這不是線程創建/管理的問題。

有沒有人知道valgrind圍繞多線程程序/繁忙的等待循環/套接字通信(或這些組合)的任何問題?

+1

忙於等待共享布爾變量不是「在單個循環中完成」,它在每個循環迭代中以幾個週期完成,並且如果您的忙循環等待TCP網絡上的往返行程,則循環可能會迭代數十億次(因此浪費數十億個CPU週期,可能在其他地方更好使用)。比你提到的任何一個更好的解決方案是等待一個條件變量,並讓回調函數發出條件變量的信號,以在數據準備就緒時喚醒你的線程。 – 2011-12-29 01:48:26

+0

我在說寫入布爾變量是在一個週期內完成的(而不是整個繁忙的等待過程)。話雖如此,我應該說寫入布爾變量是自動完成的(因爲緩存未命中等可以推動單個字節的寫入超過單個CPU週期) – Taras 2011-12-29 05:18:35

+0

什麼Jeremy說 - 忙等待是一個壞主意,條件變量是更好的,並且不太可能會變慢... – BillT 2014-03-31 17:22:30

回答

6

雖然其他答案的重點是堅持採用標準同步方法(我完全同意這一點),但我認爲應該回答你關於Valgrind的問題。

據我所知,Valgrind在多線程環境下運行時沒有問題。我相信Valgrind強制應用程序在單核上運行,但除此之外它不應該影響你的線程。

Valgrind可能對您的應用程序做了什麼,正在改變線程之間的時序和交互方式,可能會暴露代碼中的錯誤和競態條件,您在單獨運行時通常不會看到這些錯誤和競爭條件。

您應用相同的邏輯來判斷您使用的開源代碼線程框架中的錯誤不適用於我認爲的Valgrind。我建議您將這些掛起視爲代碼中的錯誤並對其進行調試,因爲這很可能是它們的原因。

作爲一個方面說明,使用互斥對您描述的問題可能是過度的。您應該調查信號量或條件變量。

祝你好運。

+0

不完全相信我的代碼中存在錯誤,但是您的文章被記錄下來。附註 - 我使用pthread庫查看條件變量,並且這些需要互斥體來停止實際條件本身的競爭條件。 – Taras 2011-12-29 22:21:33

+1

@Taras:正確的條件變量。在這種情況下,信號將成爲我的首選。比互斥體輕得多。在掛起的問題上,爲什麼不用gdb檢查掛起的進程以找出它在做什麼? – Miguel 2011-12-30 00:30:34

3

讀取/寫入布爾值不是x86上的原子操作。

見我的問題在這裏:Is volatile a proper way to make a single byte atomic in C/C++?

+0

答案確實表明,對於大多數寫入來說,布爾值是原子的:「CPU通常以原子方式讀取和寫入單個字節。」從問題的答案來看,問題不是原子性,而是所有CPU都看到新值的時候。 – Taras 2011-12-29 05:20:38

2

即使寫你的布爾是一個原子操作,周圍的其他內存compiler and the CPU are free to re-order the update訪問。忙線等待線程可能會從繁忙循環中喚醒,並發現共享數據結構有而不是實際上已被更新。

我強烈建議堅持使用可用的線程原語來編寫一致的程序,這些程序每次都按照您的需要執行。

+0

這兩個線程之間唯一共享的是布爾標誌(它被用作信號)。繁忙的等待線程只有在標誌已設置時纔會喚醒,這意味着標誌中的更改必須傳播到正在運行繁忙等待線程的CPU。鑑於此,所描述的代碼是否仍然無法正常工作? – Taras 2011-12-29 05:23:17

+0

嘿,你有信號了嗎?這可能會訣竅。 :) – sarnold 2011-12-29 05:28:07